[PR #5587] [CLOSED] fix(process): bound output draining after a stoppable command's grace #6982

Closed
opened 2026-11-01 21:44:28 +00:00 by bsakaguchi · 0 comments
Owner

📋 Pull Request Information

Original PR: https://github.com/morluto/rea/pull/5587
Author: @kaoru0822-kitauji
Created: 10/20/2026
Status: ❌ Closed

Base: main ← Head: fix/kuna-version-probe-output-drain


📝 Commits (1)

  • b7c2646 fix(process): bound output draining after a stoppable command's grace

📊 Changes

2 files changed (+43 additions, -1 deletions)

View changed files

📝 src/process/ExecFileOutput.test.ts (+35 -0)
📝 src/process/ExecFileOutput.ts (+8 -1)

📄 Description

Summary

Fixes #5557. rea doctor --provider kuna waited far past its 10-second version-probe deadline and 5-second stop grace when kuna --version printed, exited, and left a descendant holding stdout. In the issue it waited 23 s for a 22 s descendant. The shared stoppable-command helper now stops reading the command's output pipes when the grace expires, so the probe reports its timeout within deadline + grace.

Problem and expected behavior

execFileBytes (src/process/ExecFileOutput.ts) settles from execFile's callback, which Node fires on close: process exit and every stdio pipe closed. At the deadline the helper signals the command and, after STOP_GRACE_MS, sends SIGKILL. When the command had already exited, both were no-ops, and a descendant that inherited stdout kept the pipe and the promise open. KunaInstallation.ts relies on this deadline to keep doctor from hanging, and docs/tool-design.md distinguishes process exit from output closure and asks for bounded draining.

Change and scope

  • When the stop grace expires, the helper also destroys the command's stdout and stderr streams after the SIGKILL, so close fires and the existing timeout failure (ETIMEDOUT, killed: true, captured output) is reported.
  • Output completeness is not claimed: the result is the same timeout failure the deadline already produces, which the Kuna probe reports as version_unresolved.
  • Only stoppable invocations (stopSignal) change; plain execFile mode is untouched.
  • Not included: reaping the descendant. It was never owned by REA, since this helper has no process-group ownership; owned-process supervision lives in src/process/ supervisors.

Contribution scope: ordinary fix within existing boundaries; maintainer scope decision not applicable.

Contract and boundary impact

  • Semantic owner and earliest changed stage: src/process/ExecFileOutput.ts (stoppable command execution).

  • Contribution scope fit and linked maintainer decision: ordinary fix; not applicable.

  • CLI and MCP/tool-catalog contract: none; affected calls settle with their existing timeout failure.

  • Provider, bridge, target-format, or platform compatibility: all execFileOutput/execFileBytes callers using stopSignal.

  • Evidence, artifact, provenance, or reconstruction contract: none.

  • Target selection, authority, upstream behavior, and cleanup details: the command itself is still signalled and killed as before; output reading stops at the end of the grace.

  • Generated metadata, package, or installation impact: none.

  • Read existing files or captured data.

  • Launch or attach to a process.

  • Inject instrumentation or run scripts/code.

  • Change process memory, registers, files, or configuration.

  • Contact network services.

  • Recover protected code or work around protections.

  • Documentation or usability only; no target interaction.

  • Not sure yet; investigation or maintainer guidance needed.

Evidence and regression coverage

  • Test added in src/process/ExecFileOutput.test.ts, "settles after the grace when an exited command's descendant holds its output open": a real Node command prints kuna 1.833, spawns a descendant that inherits stdout/stderr for 60 s, and exits. With a 500 ms deadline, the call must reject with ETIMEDOUT / killed: true within 15 s. The test kills the recorded descendant in finally.

  • Base reproduction: with the helper change stashed, the new test hangs until its 30 s test timeout.

  • Remaining proof gaps: the issue's full rea doctor --provider kuna CLI repro was not rerun; kunaInstallation.test.ts (including its deadline-plus-grace case) passes. Not run on Linux or Windows; the test is skipped on Windows like its neighbours.

  • Observed, derived, and inferred claims remain distinguishable.

  • Artifact identity, source provenance, and failed attempts remain preserved.

  • Unsupported, incomplete, unavailable, or uncertain outcomes remain visible.

Validation performed

  • npm run test:focused -- src/process/ExecFileOutput.test.ts tests/boundary/providers/kuna/kunaInstallation.test.ts: 43 passed
  • npm run test:focused -- src/process/ExecFileOutput.test.ts with the helper change stashed: 1 failed (new test, timed out at 30000 ms)
  • npm run check:fast: passed

Compatibility, safety, and release

  • Breaking changes or migration steps: none.
  • Real Hopper/Ghidra, browser, or OS coverage: macOS arm64 with real child processes; no provider.
  • Package or release metadata impact: none.
  • Security, privacy, process, or containment review: no new processes; output reading stops after the existing grace, and the unowned descendant is left untouched.

Author readiness

Human owner: @kaoru0822-kitauji

  • I reviewed the final diff and relevant existing code, understand the change and its edge cases, and take responsibility for the complete PR, including AI-assisted work.
  • I checked the evidence above, confirmed it supports the current change, and recorded meaningful validation gaps.
  • I declare this PR ready for initial maintainer review.

Before merge

  • This PR addresses a concrete problem or an agreed enhancement.
  • Material scope and authority questions are resolved, and any required maintainer scope decision covers the final diff.
  • The PR has one focused outcome and the title follows type(scope): outcome.
  • Related issue is linked, or the reason for not linking one is stated above.
  • Tests cover changed observable behavior and meaningful failure paths.
  • Owning docs, contracts, and generated metadata are updated where needed.
  • User-visible CLI/MCP changes include representative output.
  • I checked the final diff for secrets, unrelated cleanup, and unsupported claims.

🤖 Generated with Claude Code


🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.

## 📋 Pull Request Information **Original PR:** https://github.com/morluto/rea/pull/5587 **Author:** [@kaoru0822-kitauji](https://github.com/kaoru0822-kitauji) **Created:** 10/20/2026 **Status:** ❌ Closed **Base:** `main` ← **Head:** `fix/kuna-version-probe-output-drain` --- ### 📝 Commits (1) - [`b7c2646`](https://github.com/morluto/rea/commit/b7c2646dcfd4e4b75d709be3b7d2a5ec70172130) fix(process): bound output draining after a stoppable command's grace ### 📊 Changes **2 files changed** (+43 additions, -1 deletions) <details> <summary>View changed files</summary> 📝 `src/process/ExecFileOutput.test.ts` (+35 -0) 📝 `src/process/ExecFileOutput.ts` (+8 -1) </details> ### 📄 Description ## Summary Fixes #5557. `rea doctor --provider kuna` waited far past its 10-second version-probe deadline and 5-second stop grace when `kuna --version` printed, exited, and left a descendant holding stdout. In the issue it waited 23 s for a 22 s descendant. The shared stoppable-command helper now stops reading the command's output pipes when the grace expires, so the probe reports its timeout within deadline + grace. ## Problem and expected behavior `execFileBytes` (`src/process/ExecFileOutput.ts`) settles from `execFile`'s callback, which Node fires on `close`: process exit and every stdio pipe closed. At the deadline the helper signals the command and, after `STOP_GRACE_MS`, sends SIGKILL. When the command had already exited, both were no-ops, and a descendant that inherited stdout kept the pipe and the promise open. `KunaInstallation.ts` relies on this deadline to keep doctor from hanging, and `docs/tool-design.md` distinguishes process exit from output closure and asks for bounded draining. ## Change and scope - When the stop grace expires, the helper also destroys the command's stdout and stderr streams after the SIGKILL, so `close` fires and the existing timeout failure (`ETIMEDOUT`, `killed: true`, captured output) is reported. - Output completeness is not claimed: the result is the same timeout failure the deadline already produces, which the Kuna probe reports as `version_unresolved`. - Only stoppable invocations (`stopSignal`) change; plain `execFile` mode is untouched. - Not included: reaping the descendant. It was never owned by REA, since this helper has no process-group ownership; owned-process supervision lives in `src/process/` supervisors. Contribution scope: ordinary fix within existing boundaries; maintainer scope decision not applicable. ## Contract and boundary impact - Semantic owner and earliest changed stage: `src/process/ExecFileOutput.ts` (stoppable command execution). - Contribution scope fit and linked maintainer decision: ordinary fix; not applicable. - CLI and MCP/tool-catalog contract: none; affected calls settle with their existing timeout failure. - Provider, bridge, target-format, or platform compatibility: all `execFileOutput`/`execFileBytes` callers using `stopSignal`. - Evidence, artifact, provenance, or reconstruction contract: none. - Target selection, authority, upstream behavior, and cleanup details: the command itself is still signalled and killed as before; output reading stops at the end of the grace. - Generated metadata, package, or installation impact: none. - [ ] Read existing files or captured data. - [x] Launch or attach to a process. - [ ] Inject instrumentation or run scripts/code. - [ ] Change process memory, registers, files, or configuration. - [ ] Contact network services. - [ ] Recover protected code or work around protections. - [ ] Documentation or usability only; no target interaction. - [ ] Not sure yet; investigation or maintainer guidance needed. ## Evidence and regression coverage - Test added in `src/process/ExecFileOutput.test.ts`, "settles after the grace when an exited command's descendant holds its output open": a real Node command prints `kuna 1.833`, spawns a descendant that inherits stdout/stderr for 60 s, and exits. With a 500 ms deadline, the call must reject with `ETIMEDOUT` / `killed: true` within 15 s. The test kills the recorded descendant in `finally`. - Base reproduction: with the helper change stashed, the new test hangs until its 30 s test timeout. - Remaining proof gaps: the issue's full `rea doctor --provider kuna` CLI repro was not rerun; `kunaInstallation.test.ts` (including its deadline-plus-grace case) passes. Not run on Linux or Windows; the test is skipped on Windows like its neighbours. - [x] Observed, derived, and inferred claims remain distinguishable. - [x] Artifact identity, source provenance, and failed attempts remain preserved. - [x] Unsupported, incomplete, unavailable, or uncertain outcomes remain visible. ## Validation performed - `npm run test:focused -- src/process/ExecFileOutput.test.ts tests/boundary/providers/kuna/kunaInstallation.test.ts`: 43 passed - `npm run test:focused -- src/process/ExecFileOutput.test.ts` with the helper change stashed: 1 failed (new test, timed out at 30000 ms) - `npm run check:fast`: passed ## Compatibility, safety, and release - Breaking changes or migration steps: none. - Real Hopper/Ghidra, browser, or OS coverage: macOS arm64 with real child processes; no provider. - Package or release metadata impact: none. - Security, privacy, process, or containment review: no new processes; output reading stops after the existing grace, and the unowned descendant is left untouched. ## Author readiness Human owner: @kaoru0822-kitauji - [x] I reviewed the final diff and relevant existing code, understand the change and its edge cases, and take responsibility for the complete PR, including AI-assisted work. - [x] I checked the evidence above, confirmed it supports the current change, and recorded meaningful validation gaps. - [x] I declare this PR ready for initial maintainer review. ## Before merge - [ ] This PR addresses a concrete problem or an agreed enhancement. - [ ] Material scope and authority questions are resolved, and any required maintainer scope decision covers the final diff. - [ ] The PR has one focused outcome and the title follows `type(scope): outcome`. - [ ] Related issue is linked, or the reason for not linking one is stated above. - [ ] Tests cover changed observable behavior and meaningful failure paths. - [ ] Owning docs, contracts, and generated metadata are updated where needed. - [ ] User-visible CLI/MCP changes include representative output. - [ ] I checked the final diff for secrets, unrelated cleanup, and unsupported claims. 🤖 Generated with [Claude Code](https://claude.com/claude-code) --- <sub>🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.</sub>
bsakaguchi 2026-11-01 21:44:28 +00:00
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
morluto/rea#6982
No description provided.