Skip to content

fix(claude): prove a stopped chat's child processes gone when they exit with it - #22918

Merged
brennanb2025 merged 2 commits into
mainfrom
brennanb2025/claude-stop-proves-exited-children
Sep 25, 2026
Merged

brennanb2025 merged 2 commits into
mainfrom
brennanb2025/claude-stop-proves-exited-children

Conversation

@brennanb2025

@brennanb2025 brennanb2025 commented Sep 25, 2026 •

Copy link
Copy Markdown
Contributor
Files Added Deleted Net
Test 3 $\color{#1a7f37}{\Huge{\mathbf{+}}}$​80 $\color{#cf222e}{\Huge{\mathbf{−}}}$​20 $\color{#1a7f37}{\Huge{\mathbf{+}}}$​60
Prod 1 $\color{#1a7f37}{\Huge{\mathbf{+}}}$​32 $\color{#cf222e}{\Huge{\mathbf{−}}}$​25 $\color{#1a7f37}{\Huge{\mathbf{+}}}$​7

ELI5

When Orca stops a Claude chat's process, it also checks that every process Claude started is gone. That check was broken for the most common case: Claude exits cleanly and takes its short-lived helpers with it. Orca looked for them, found nothing every time, and still answered "can't tell", so every normal stop was reported as uncertain and took an extra 3.5 seconds. Now "not there on two fresh looks" counts as gone, and the check finishes in about 0.15 seconds.

What Changed

  • Before: every idle stop of a Claude chat ended with its child processes "unverifiable", after waiting out the full 3.5 s verification window. That uncertain verdict is what surfaced as "provider close unproven" before fix(claude): let a Claude chat start again after its root exited with unverifiable descendants #22802, and it still made every stop take the fallback route that fix(claude): let a Claude chat start again after its root exited with unverifiable descendants #22802 added.
  • After: children that exited with Claude are proven gone within a few polls (measured about 150 ms), and the stop reports a clean, proven exit. A child that is still running is still found, signalled, and waited for as before.
  • Mechanism, terminateDescendantSnapshotWithVerdict in pty-descendant-exit-verification.ts (identity-checked mode, used by Claude's stop and the daemon's terminal shutdown): a snapshotted child used to count as gone only after it had appeared in one of the verifier's own process-table reads and then been missing from two. A child that exited before the first read never appeared, so it could never be proven gone. Now the snapshot, which is itself a process-table read that saw the child alive, counts as the first sighting. An absence counts only from a read that started after the child was last seen. Two such absences prove it gone.

Why

The old rule was guarding against a real hazard: an absence taken from a read that started before the child was seen, such as a shared or in-flight read in the daemon's batched shutdown, which cannot list a child forked after it began. Every capture already records when its scan started (capturedAtMs), so the new rule guards that hazard directly. A read begun before the last sighting is ignored. A full ps -axo scan begun after it cannot omit a live process: a failed or truncated read is rejected, never parsed as a short table. That removes the "never seen again, so never provable" trap without weakening the protection.

Alternatives considered:

Linked Issue

No separate issue. This is the follow-up from #22802's investigation. The rule came in with #22232 (daemon terminal-descendant reaping), which made every graceful Claude stop unverifiable. Before it, the identity-checked verifier accepted two consecutive empty reads.

Visual Proof

N/A: process-lifecycle behavior with no UI change. Live evidence instead (isolated dev instance, real Claude Code sessions, temporary logging in the verifier; QA build only):

Build Idle stop of a finished chat
main snapshot held 1 child; 37 complete reads (~1,150 rows each) over 3.6 s, child absent from every one; verdict unverifiable; ladder reaped:false, exited:true, treeVerdict:"unverifiable"
this PR snapshot held 1–2 children; verdict exited after 150–166 ms (two fresh absences each); ladder treeVerdict:"exited"; returning to the chat restarts Claude and the next send is answered

Kill path, this PR: closing a chat tab while a background sleep 150 ran. Claude did not exit on its own, so the ladder forced it (reaped:true). The running sleep (in the snapshot) was gone 8 s later, and all 3 children were proven gone in 289 ms.

Testing

  • pty-descendant-exit-verification.test.ts:

    • New: a child seen only in the snapshot is proven gone within 200 ms once two later reads miss it, with no signal sent. Fails without the fix (still pending, since the old rule waits out the window).
    • Replaced fix(daemon): reap terminal descendants during shutdown #22232's "keeps an omitted target unverifiable when partial reads never show its identity" with the hazard it stood for: a read that started before the child was seen does not count, and the verdict stays unverifiable with no signal.
    • New: a matching read that started earlier but finished later does not move the child's last sighting back, so a read begun between the two sightings still does not count as an absence.
    • New: the read taken after the verification deadline counts as an absence the same way the polling reads do, so it can supply the second one.
  • Existing tests reused one mocked capture stamped with the snapshot's own time for every read. A real scan is stamped when it starts, so the mocks in pty-descendant-exit-verification.test.ts, pty-descendant-termination.test.ts (recycled pid) and daemon/terminal-descendant-shutdown.test.ts now stamp each read after the snapshot. The assertions are unchanged, including the daemon's shared-read count.

  • 126 tests across 9 suites (the verifier, PTY termination, daemon shutdown, daemon reaping leak, both Claude exit-proof suites, Claude close, stream-json close and Codex app-server teardown) pass, plus 69 in 5 more suites that reach the verifier. pnpm tc:node, oxlint and check:code-quality:changed are clean.

  • I manually tested these changes locally

  • Automated tests added/updated, or explained why not below

AI Disclosure

Review

Agent skill upstream boundary

  • Not applicable, or this change follows docs/reference/agent-skill-sharing-upstream-boundary.md and copies or mechanically translates no upstream skill-installer source, tests, fixtures, registry entries, path tables, comments, or documentation.

Notes

  • Daemon terminal shutdown uses the same verifier: a terminal child that exits during shutdown is now proven gone instead of left "unverifiable". Its batched shared read is still honored for children seen after it started.
  • Windows is unaffected: its verification path does not use this function.
  • Linux uses the same ps -axo scan; SSH remotes are unaffected, since this runs on the execution host.
  • Performance: every Claude stop is about 3.3 s faster, with fewer process-table scans.

Checklist

  • This PR is small and focused
  • I explained what changed and why (ELI5, the user-facing before/after, the mechanism, and why over the alternatives)
  • Before/after screenshots or videos attached for UI changes, or N/A with reason
  • Self-reviewed for correctness, security, and performance
  • Cross-platform, SSH/remote, and path/shortcut impact considered (or N/A)
  • pnpm lint, pnpm typecheck, pnpm test, and pnpm build pass (or CI will cover; local preferred)

…it with it

Stopping a Claude chat snapshots its child processes, closes Claude, and then verifies each child is gone before the stop counts as proven. The verifier only accepted a child as gone after that child had appeared in one of its own process-table reads. When Claude exits gracefully it takes its short-lived children with it before the first read, so none of them was ever seen again. Every read confirmed them absent, and the verdict was still "unverifiable" after the full 3.5 s window. Measured live: 37 complete reads, target absent from all, verdict unverifiable, on every idle stop.

The snapshot is itself a table read that saw each child alive, so it now counts as the first sighting. An absence counts only from a read that started after the child was last seen, which keeps what the old rule protected against: a shared or in-flight read begun before the snapshot cannot list a child forked since. Two such absences prove a child gone. Live, the same stop now proves the tree gone in about 150 ms.

The daemon's terminal shutdown uses the same verifier and gets the same rule. Test reads that reused one capture stamped with the snapshot's own time now stamp each read when it starts, as real scans do.
@coderabbitai

coderabbitai Bot commented Sep 25, 2026 •

Copy link
Copy Markdown
Contributor

Review in Change Stack →

Navigate logical layers of code changes, visualize relationships, and explore their blast radius.

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Advanced

Run ID: 381c9dae-4361-4bc1-b750-568c1b29fa12

📥 Commits

Reviewing files that changed from the base of the PR and between 84f592c and b63384f.

📒 Files selected for processing (2)
  • src/main/pty-descendant-exit-verification.test.ts
  • src/main/pty-descendant-exit-verification.ts
🚧 Files skipped from review as they are similar to previous changes (2)
  • src/main/pty-descendant-exit-verification.ts
  • src/main/pty-descendant-exit-verification.test.ts

Included review availability: Your plan provides up to 10 included reviews per hour; 5 remain after this review.


📝 Walkthrough

Walkthrough

Identity-required descendant verification now counts a missing process only when the process-table capture started after that descendant was last seen. It requires two qualifying absences per descendant for an exited verdict. The tests cover snapshot-only targets, stale and out-of-order captures, final-read timing, and updated process-table mocks.

Priority: ⬇️ Low

Merge Risk: ⚪ Minimal · up to b6338

The descendant-exit verification change is mergeable after normal checks; no actionable issue remains from this review.

Architecture Summary

Architecture risk: 🔵 Low · up to 84f59

The change affects 1 system.

Changed systems: src

Architecture concerns
No architecture-level concerns identified.

Review details

Systems and components

  • observed — src (service) was modified; 4 changed files map to changed impact.

Before / after behavior

  • observed — Modified behavior in src/main/daemon/terminal-descendant-shutdown.test.ts: The test replaces precomputed mockResolvedValueOnce and mockResolvedValue captures with implementations that return captures when invoked, timestamped at Date.now() + 1 for both the initial rows and subsequent empty reads.
  • observed — Modified behavior in src/main/pty-descendant-exit-verification.test.ts: The sequential process-table read mocks now use asynchronous implementations instead of mockResolvedValueOnce/mockResolvedValue; the returned captures and read order are unchanged.
  • observed — Modified behavior in src/main/pty-descendant-exit-verification.test.ts: Adds a test where two later empty reads establish that a snapshot-only target exited without signaling it. Replaces the partial-read test, which had an identity read followed by misses and expected a signal for the seen target, with a stale-read case: a read captured before the snapshot does not establish absence, and the test expects an unverifiable verdict with no signal.
  • observed — Modified behavior in src/main/pty-descendant-exit-verification.test.ts: Changes the assertion from expecting a signal for the target seen in a partial read to asserting that no signal was sent.
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Description check ⚠️ Warning The description is detailed and covers the change, rationale, testing, scope, and platform impact. However, the required Linked Issue entry is missing, and the required command-validation checklist re… Add the required issue reference in the form Fixes #<issue-number>. Complete the pnpm lint, pnpm typecheck, pnpm test, and pnpm build checks, or state that CI will cover each unchecked command.
✅ Passed checks (4 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 1 functions across 4 files.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Title check ✅ Passed The title clearly describes the main change: proving that child processes are gone when a stopped Claude chat exits.
Full details: Description check

Explanation

The description is detailed and covers the change, rationale, testing, scope, and platform impact. However, the required Linked Issue entry is missing, and the required command-validation checklist remains unchecked.

  • Fix all pre-merge checks with AI
✨ Finishing Touches
📝 Generate docstrings
  • Commit to this branch
  • Create a new PR

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 2


ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Advanced

Run ID: c8f49318-b41f-4b26-864c-e1cedae5608e

📥 Commits

Reviewing files that changed from the base of the PR and between 8fb13ed and 84f592c.

📒 Files selected for processing (4)
  • src/main/daemon/terminal-descendant-shutdown.test.ts
  • src/main/pty-descendant-exit-verification.test.ts
  • src/main/pty-descendant-exit-verification.ts
  • src/main/pty-descendant-termination.test.ts

Included review availability: Your plan provides up to 10 included reviews per hour; 4 remain after this review.

@pullfrog pullfrog Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

ℹ️ No critical issues — one non-blocking soundness note inline.

Reviewed changes

  • Prove snapshot-only descendants gone — terminateDescendantSnapshotWithVerdict now seeds a per-PID lastSeenAtMs from the snapshot's capture boundary and counts an absence only from a read whose capturedAtMs is newer, so a child that exits with Claude before the verifier's first read can be proven gone. This is the only production change.
  • New regression test for the clean-exit path — pty-descendant-exit-verification.test.ts adds a test that a snapshot-only child is proven exited within 200 ms with no signal sent; it fails under the old rule (which waits out the window).
  • Rewritten stale-read test — the prior "omitted target stays unverifiable" test is replaced by one asserting a read whose capturedAtMs predates the snapshot boundary is not counted; it pins the new timestamp guard.
  • Test mocks re-stamped after the snapshot — pty-descendant-termination.test.ts and daemon/terminal-descendant-shutdown.test.ts now stamp verification reads after the walks that produced each snapshot, matching real scan timing; assertions (including the daemon's shared-read count) are unchanged.

I read the full diff, traced both identity-checked callers (claude-agent-sdk-exit-proof.ts and daemon/terminal-descendant-shutdown.ts), and ran the three changed test files: 57/57 pass. The logic is sound and the fix addresses the reported regression without weakening the identity checks.

Pullfrog  | Fix all ➔ | Fix 👍s ➔ | View workflow run | Using DeepSeek Flash (free via Pullfrog for OSS) | 𝕏

Comment on lines +127 to +135
// The snapshot is itself a table read that saw each target alive. An absence is evidence only
// from a read that started after the target was last seen: a shared or in-flight read begun
// earlier can miss a descendant forked since, but a later full scan cannot miss a live one.
const lastSeenAtMs = new Map(
snapshot.descendants.map((row) => [
row.pid,
snapshot.capturedAtMsByPid?.[String(row.pid)] ?? snapshot.capturedAtMs
])
)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

The freshness guard compares a read's scan start against the snapshot's scan start, but the snapshot observed each descendant at scan enumeration, which is at or after that start. A shared/in-flight read begun after the snapshot started but whose ps ran before a descendant was forked can therefore pass capture.capturedAtMs > lastSeenAtMs and be counted as a fresh absence. Today the daemon's 25 ms shared-capture linger versus the verifier's 50 ms poll cadence admits at most one such absence, so provenAbsent() (which requires two) cannot fire — but that safety comes from those two unrelated constants, not from this guard.

Technical details
# Freshness boundary uses scan start, not observation time

## Affected sites
- `src/main/pty-descendant-exit-verification.ts:130-135` — `lastSeenAtMs` seeded from `snapshot.capturedAtMs` (a scan start), so it under-states when the descendant was actually observed by up to the snapshot scan's duration.
- `src/main/pty-descendant-exit-verification.ts:165` — `capture.capturedAtMs > lastSeenAtMs` admits any read whose scan started after the snapshot started, including one whose `ps` enumerated before the descendant was forked.
- `src/main/pty-descendant-termination.ts:53` — `capturedAtMs = Date.now()` is stamped before `execFile('ps', ...)` spawns; no completion/enumeration boundary is recorded anywhere.
- `src/main/daemon/terminal-descendant-shutdown.ts:14-38` — the shared capture can be created by an earlier terminal's verifier before a later terminal's snapshot completes, so a later verifier can be served a read that predates its own snapshot's observation of a late-forked child.

## Required outcome
- A fresh absence should mean "this read could have listed the descendant and did not", i.e. its enumeration is after the snapshot's enumeration. The current check only guarantees the read started after the snapshot started.

## Suggested approach (optional)
- Record a scan completion boundary on `ProcessTableCapture`/`DescendantSnapshot` and seed `lastSeenAtMs` from it (an upper bound on enumeration), or have `captureDescendantSnapshot` pass its completion time into `collectDescendantRows`.
- At minimum, correct the comment at `:127-129`: the guard does not by itself exclude "a read begun earlier [than the child was forked]"; the shared-read slot's 25 ms linger is what currently bounds it.

## Open questions for the human (optional)
- Is the 25 ms linger in `readShutdownProcessTable` intended as a safety margin coupled to the 50 ms verifier poll, or incidental? If incidental, documenting it as load-bearing (or removing the dependence) would be worthwhile.

…absence

A matching read that started earlier but resolved later could move a target's
last sighting back and let an older absence count; the sighting now only moves
forward. The read after the deadline now records its absences the same way the
polling loop does, so a second qualifying absence there proves the target gone.

@pullfrog pullfrog Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

ℹ️ No new issues in the follow-up commits; the prior review's open soundness note is unaffected and remains non-blocking.

Reviewed changes

  • Made a descendant's last sighting monotonic — recordIdentityObservation advances lastSeenAtMs with Math.max, so a read that started earlier but resolves later can no longer move the sighting backward and reopen an absence window.
  • Counted the post-deadline read as an absence — the final read now flows through the same observation helper, letting it supply the second absence that proves a snapshot-only child gone instead of only deciding live/unverifiable.
  • Extracted the shared observation loop — the polling loop and the final read now use one recordIdentityObservation helper rather than duplicated inline state updates.
  • Pinned both behaviors with tests — the latest-sighting-retention and final-read-absence tests both fail against 84f592c and pass at the head.

Pullfrog  | Fix it ➔ | View workflow run | Using DeepSeek Flash (free via Pullfrog for OSS) | 𝕏

@brennanb2025

Copy link
Copy Markdown
Contributor Author

Review status: ready to merge (head b63384f)

Summary: two review loops. The first made one follow-up fix; the second found nothing to change. Readiness checklist passed at the start and at the end. CI is green and the merge state is clean.

Does it fix the problem? Yes. On the base code, a shell whose two children exit along with it came back "unverifiable" after about 4 s. With this PR, the same case comes back "exited" in 170–212 ms (real ps on macOS, 3 runs). The Claude stop now takes the normal proven-close path instead of the uncertain one. A child that is still alive is still found, signalled and escalated as before.

Is the approach right? Yes, keep it. The rule from #22232 caused the bug: it insisted on a sighting in the verifier's own reads, which a child that exited with Claude can never give. The PR replaces that rule with a direct check on the hazard it stood in for, a read that started before the child was last seen. It does not add a second guard beside the old one. Two absences are still required.

Follow-up fix in this review (b63384f). From two CodeRabbit findings:

  • A child's last-sighting time can now only move forward.
  • The read taken after the verification deadline now counts as an absence in the same way the polling reads do.

Each fix has a test, and each test fails when only its fix is reverted.

Checked, not defects:

  • Daemon terminal shutdown shares one table read across terminals. A shared read that started before a child's snapshot is ignored. The next read is always fresh, so a verifier cannot be starved.
  • A full ps -axo scan did not miss a long-lived process: 300 scans taken while short-lived processes were being spawned all listed it.
  • The existing comment "a refresh can omit a still-live row" is about the parent-child walk, not table membership, so it does not conflict with this change.
  • Codex callers use the non-identity mode, which is unchanged.
  • Performance: about 4 table scans per stop instead of about 37, and about 3.3 s faster.

Open, not blocking:

  • Needs a decision: if ps ever exited successfully with empty output, two such reads would now prove a snapshot-only child gone. The rule before this PR already accepted that for any child it had seen. There is no evidence this happens, so no guard was added.
  • Pre-existing: a live child that changes its process group after the snapshot stops matching its identity and is counted as gone. The old rule gave the same result.

Validation

  • 126 tests across 9 suites passed, plus 69 in 5 more suites that reach the verifier, all run by explicit path.
  • The core tests were checked by reverting each part of the fix.
  • pnpm tc:node, oxlint and check:code-quality:changed are clean.
  • Live Electron QA with real Claude sessions was done by the author (traces in the PR body) and was not repeated.

Not covered:

  • No run on Linux or on a loaded host.
  • This review did not read the kernel or ps source directly.

@brennanb2025

Copy link
Copy Markdown
Contributor Author

Review status: ready to merge, no changes needed

This review pushed no commits. Head is still b63384ff92, and CI is green.

What was checked

  • Fixes the reported problem. A child process that exits together with a cleanly closing Claude is now proven gone instead of leaving every stop "unverifiable" after the full 3.5 s window. Reverting the fix makes the three new tests fail. Removing either half of it on its own (the "read must start after the last sighting" rule, or keeping the latest sighting) also turns a test red.
  • No false "gone" verdict.
    • Every table read used here is stamped when its scan starts.
    • The daemon's shared read can hand out a scan that began before the snapshot, but that read can supply at most the first absence. The second always comes from a scan that started after the child was last seen.
    • A full ps -axo scan that starts after a process was seen cannot omit it while it is still alive (macOS reads the process list in one copy; Linux walks /proc in pid order).
    • Failed, timed-out or oversized reads are still rejected. Duplicate-pid reads still end "unverifiable".
    • Mode without identity checks (the Codex callers) is unchanged.
  • Design. The old rule stood in for "a read happened after the child was known alive" by requiring the child to appear in one of the verifier's own reads. It ignored the snapshot, which is itself such a read. This PR stores that fact directly (a per-child last-sighting time, and absences counted only from reads that started after it). That removes the bug class instead of adding a guard beside the old one. We considered a single-pid liveness probe, a scan sequence number and process-group checks as replacements; none is clearly better.
  • Regressions and performance. Only Claude's stop check and the daemon's terminal shutdown use this mode. Each read does slightly less work than before, and stops that used to wait out the full window now settle in a few polls.
  • Readiness checklist at the start and end of the review: READY both times, no blocking findings. pnpm tc:node, oxlint and check:code-quality:changed are clean, and the related suites pass. The one local failure is claude-structured-real-cli.test.ts, which drives the real installed Claude CLI and is skipped in CI.

Live Electron check

Setup: isolated background dev instances, real Claude Code, and the same steps on each build. The only difference between builds was the verifier file. A temporary log line showed the verdict (QA build only; removed afterwards). The idle-release timer was shortened to 10 s for the rig.

Scenario main verifier This PR
Idle stop, 1 child in the snapshot unverifiable after 3,599–3,868 ms exited in 733 ms
Idle stop, 2–3 children unverifiable after 4,437 ms exited in 636 ms and 2,708 ms
Close the tab while a background python3 … sleep(150) runs (3 children) unverifiable after 3,565 ms, although every pid was gone exited in 429 ms; the python, its shell and Claude all gone
Return to the chat and send again Claude restarts and answers Claude restarts and answers, and a third exchange works too

Verdict logs, main verifier:

before-logs.png

Verdict logs, this PR:

after-logs.png

Chat with the background command running before the tab is closed (main, then this PR):

before-kill-before-close.png
after-kill-before-close.png

Chat answering again after an idle stop and restart (main, then this PR):

before-idle-restarted.png
after-idle-restarted.png

Existing issue seen during QA (not caused by this PR)

After an idle stop and restart, the composer can show a red line: "Expected runtime fence 1; the session is at 3." The message is still delivered and answered, and the line stays until the next send.

It comes from the chat screen's send path. The screen keeps the session's old fence number while you are away. Its first send after you return is refused, then resent with the current fence and accepted, but the error text is never cleared.

It appears on main too. With the main verifier it appeared in 2 of 3 runs, on slow 3.6–4.4 s stops, so stop speed does not cause it. With this PR it appeared in 3 of 3 runs. It also appeared once on a stop with no child processes, which never reaches the code this PR changes. It only affects the experimental structured chat, and #22812 addresses this exact message.

Screenshot with the main verifier:

fence-before-2.png

Known limits, left unchanged on purpose

  • A ps that exits successfully with zero rows would now count as an absence. There is no evidence this happens (ps -ax always lists itself), so no guard was added.
  • For merged Claude snapshots, a child's starting sighting time is its earliest sighting, not its latest. This is safe because Claude's reads never start before the verifier is called. It would need revisiting only if Claude's stop check started using the daemon's shared reads.
  • If a child moves to a different process group after the snapshot, it stops matching its recorded identity and counts as gone. The same is true on main, and was before fix(daemon): reap terminal descendants during shutdown #22232.
  • Not exercised live: SSH hosts (this runs on the execution host, and nothing on the wire changes) and Linux (it uses the same ps -axo scan; covered by unit tests and CI).

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant