fix(lsp): wrap post-Popen init in cleanup guard to prevent orphaned processes #11237

Merged
HAL9000 merged 5 commits from bugfix/m3.6.0-lsp-7044-subprocess-cleanup into master 2026-05-28 16:18:26 +00:00

5 Commits

Author SHA1 Message Date
HAL9000 22831e4f50 fix(lsp): make post-spawn logger.info mock selective and remove tdd_expected_fail
CI / lint (pull_request) Successful in 53s
CI / quality (pull_request) Successful in 54s
CI / typecheck (pull_request) Successful in 1m30s
CI / security (pull_request) Successful in 1m32s
CI / helm (pull_request) Successful in 29s
CI / build (pull_request) Successful in 33s
CI / push-validation (pull_request) Successful in 30s
CI / integration_tests (pull_request) Successful in 2m57s
CI / unit_tests (pull_request) Successful in 4m38s
CI / docker (pull_request) Successful in 1m25s
CI / coverage (pull_request) Successful in 13m7s
CI / status-check (pull_request) Successful in 6s
The _info_raises side_effect previously raised RuntimeError on every
logger.info call, which caused the pre-Popen "lsp.transport.starting"
call (transport.py:109) to raise before subprocess.Popen was ever
reached. This meant the cleanup guard at lines 160-186 was never
exercised, making the scenario a permanent no-op TDD stub rather than
a genuine regression guard.

Fix: make _info_raises conditional on the first positional argument
being "lsp.transport.started" (the post-Popen message). The pre-Popen
"lsp.transport.starting" call now passes through normally, Popen
succeeds (mocked), and the RuntimeError is raised inside the guarded
try/except block, triggering terminate() + wait() cleanup.

Remove @tdd_expected_fail from the scenario since the cleanup code at
transport.py:160-186 is in place and the scenario now passes.

Closes #7044
2026-05-28 11:36:14 -04:00
HAL9000 c2459772eb fix(lsp): restore missing else branch for stderr in _make_mock_process
CI / helm (pull_request) Successful in 33s
CI / build (pull_request) Successful in 34s
CI / lint (pull_request) Successful in 47s
CI / typecheck (pull_request) Successful in 1m8s
CI / quality (pull_request) Successful in 1m14s
CI / security (pull_request) Successful in 1m27s
CI / push-validation (pull_request) Successful in 28s
CI / integration_tests (pull_request) Successful in 3m0s
CI / unit_tests (pull_request) Successful in 4m40s
CI / docker (pull_request) Successful in 1m28s
CI / coverage (pull_request) Successful in 11m27s
CI / status-check (pull_request) Successful in 3s
The `else: proc.stderr = stderr` clause was dropped when the helper
was extracted into `_ltcov_helpers.py`. `stdin` and `stdout` both
have the corresponding else branches; this restores parity so that
callers passing a non-"auto" stderr value have it honoured.
2026-05-28 11:03:11 -04:00
HAL9000 a61d132ac0 fix(lsp): fix relative imports and remove unused imports in BDD step files
CI / lint (pull_request) Successful in 49s
CI / quality (pull_request) Successful in 1m4s
CI / security (pull_request) Successful in 1m6s
CI / typecheck (pull_request) Successful in 1m10s
CI / integration_tests (pull_request) Successful in 4m5s
CI / unit_tests (pull_request) Successful in 4m44s
CI / build (pull_request) Successful in 31s
CI / helm (pull_request) Successful in 25s
CI / push-validation (pull_request) Successful in 20s
CI / coverage (pull_request) Has started running
CI / docker (pull_request) Successful in 2m20s
CI / status-check (pull_request) Has been cancelled
- features/steps/lsp_transport_coverage_steps.py: change relative import
  `from ._ltcov_helpers import build_lsp_frame, ...` to absolute
  `from _ltcov_helpers import make_mock_process ...`, removing unused
  `build_lsp_frame`
- features/steps/lsp_transport_post_spawn_cleanup_steps.py: remove unused
  `subprocess` and `MagicMock` imports; fix relative import to absolute;
  collapse short assert messages to single lines; remove trailing blank line

Behave's exec_file loader does not set __name__ in globals, causing relative
imports to raise KeyError at step-module load time. Absolute imports work
because Behave adds features/steps/ to sys.path.

ISSUES CLOSED: #11237
2026-05-28 10:50:24 -04:00
controller-ci-rerun 421fa4060c chore: re-trigger CI [controller] 2026-05-28 10:50:24 -04:00
HAL9000 daf90ba764 fix(lsp): wrap post-Popen init in cleanup guard to prevent orphaned processes
Closes #7044

The StdioTransport.start() method had an unprotected logger.info() call
after successful Popen(). If that call raised, the subprocess would
leak as an orphaned process. Wrap all post-spawn initialization in a
try/except guard: on any exception after spawn, terminate and wait for
the process (with kill fallback), reset state to None, then re-raise
so callers still get proper error semantics.

The existing stop() method cleanup pattern (terminate → wait → kill) is
mirrored here for consistency across the transport lifecycle.

Tests added:
  - TDD scenario with @tdd_issue_7044 verifying subprocess cleanup on
    post-Popen exception and state reset to None
  - Explicit is_alive() scenarios covering both alive and not-alive states

Refactoring:
  - Extracted _make_mock_process and _build_lsp_frame helpers into a
    shared _ltcov_helpers module to keep step files under the 500-line
    CONTRIBUTING.md limit.

ISSUES CLOSED: #7044
2026-05-28 10:50:24 -04:00