refactor(subprocess): keep terminate() as the seam's only termination verb
Delete kill(signal?) from SubprocessHandle: consumers stop a process only through terminate()'s tree-scoped SIGTERM→graceMs→SIGKILL escalation (idempotent, also driven by the spec's abort signal, a no-op once the tree is gone). The single-signal verb had exactly one consumer family — lsp-local — and what it bought there was a private re-implementation of the same escalation. The internal kill closure stays in spawn.ts as the dispose ladder's tier primitive; terminate() now routes through it too. lsp-local collapses onto the seam's escalation: - LspConnection replaces its terminate()/kill() pair with one terminate() that delegates to handle.terminate(). Behavior change: the framing-failure path terminates instead of instant SIGKILL, so a misbehaving server now gets SIGTERM plus the killGraceMs window to flush before SIGKILL. - ConnectionSpec.pipeDrainGraceMs becomes killGraceMs: one grace, the spawn spec's graceMs, drives both the escalation window and post-exit pipe draining (the provider already passed killGraceMs for it). - LspInstance.forceTerminate() drops its hand-rolled bounded first wait (LSP_KILL_GRACE) and escalateProcessTree (deleted with its export and unit test): the seam's escalation already commits to SIGKILL after killGraceMs, so only the unbounded quiescence awaits stay load-bearing. Tests: kill()-shaped spawn specs become terminate()-shaped or fold into the terminate() suites (group-wide delivery; the settled no-op case was already pinned by 'terminate() after the tree died'); tree-survivor coverage is intact. A stderr-'inherit' disposition test completes the stdout/stderr symmetry so the scoped subprocess+lsp coverage gate stands alone instead of leaning on subagent-acp's cross-package runs. Docs: SubprocessHandle type-equiv block, seam/impl/group READMEs, and the consumer-migration Agent Note lose the kill(signal?) vocabulary (zh pairs re-recorded); cordis api/services catalogs regenerated.
This commit is contained in:
@@ -2,5 +2,5 @@
|
||||
# side as of the last confirmed-consistent state. Both languages carry equal authority;
|
||||
# after editing either side, bring the other along and re-record with:
|
||||
# pnpm run verify-translation-pairing --write
|
||||
subprocess.md: cdd4507c7d37f47ca243ddf38114f5aa6b6f3ad1
|
||||
subprocess.zh.md: 78325c3255c42ed591bbd98fdbdb4fdfcba48202
|
||||
subprocess.md: b6c316b079177f052302c0b456d300bc54d9ccb6
|
||||
subprocess.zh.md: 421ab335b02dfbd20eabd121b96e290eb12d172c
|
||||
@@ -132,7 +132,7 @@ interface SubprocessSpawnSpec {
|
||||
|
||||
## Handles: streams, readers, and tree-scoped termination
|
||||
|
||||
A spawn returns a live handle immediately. Collect-mode readers take whole-stream byte offsets and never consume, so independent readers cannot steal one another's deltas; piped streams belong to the caller. Termination is tree-scoped on every platform: `kill(signal)` sends one signal Node-style, `terminate()` escalates SIGTERM→grace→SIGKILL, `waitForExit()` observes the whole tree, and `dispose(graces)` runs the cooperative stdin-EOF→SIGTERM→SIGKILL ladder out-of-process children need.
|
||||
A spawn returns a live handle immediately. Collect-mode readers take whole-stream byte offsets and never consume, so independent readers cannot steal one another's deltas; piped streams belong to the caller. Termination is tree-scoped on every platform: `terminate()` — the only termination verb — escalates SIGTERM→grace→SIGKILL, `waitForExit()` observes the whole tree, and `dispose(graces)` runs the cooperative stdin-EOF→SIGTERM→SIGKILL ladder out-of-process children need.
|
||||
|
||||
```ts type-equiv
|
||||
/**
|
||||
@@ -157,17 +157,11 @@ interface SubprocessHandle {
|
||||
readonly collected: SubprocessCollectedOutputs
|
||||
/** Resolves at process close with exit facts; rejects only for spawn-level failures. */
|
||||
readonly done: Promise<SubprocessOutcome>
|
||||
/**
|
||||
* Send one signal to the process tree, Node-style — no escalation, no
|
||||
* timers. A no-op after the outcome has settled (the pid may be reused).
|
||||
* @param signal - the signal to deliver (default `SIGTERM`; Windows
|
||||
* force-terminates the tree for any value).
|
||||
*/
|
||||
kill(signal?: NodeJS.Signals): void
|
||||
/**
|
||||
* Begin the SIGTERM → `graceMs` → SIGKILL escalation on the process tree
|
||||
* (Windows force-terminates immediately). Idempotent; also triggered by the
|
||||
* spec's abort signal.
|
||||
* (Windows force-terminates immediately) — the seam's only termination
|
||||
* verb. Idempotent, a no-op once the tree is gone (the pid may be reused),
|
||||
* and also triggered by the spec's abort signal.
|
||||
*/
|
||||
terminate(): void
|
||||
/**
|
||||
|
||||
@@ -132,7 +132,7 @@ interface SubprocessSpawnSpec {
|
||||
|
||||
## 句柄:流、读取器与以进程树为范围的终止
|
||||
|
||||
spawn 会立即返回一个实时句柄。收集模式的读取器接受全流字节偏移量且从不消费,因此独立的读取器不会抢走彼此的增量;管道化的流归调用方所有。终止在每个平台上都以进程树为范围:`kill(signal)` 以 Node 风格只发送一个信号,`terminate()` 执行 SIGTERM→宽限期→SIGKILL 升级,`waitForExit()` 观察整棵进程树,`dispose(graces)` 运行进程外子进程所需的协作式 stdin EOF→SIGTERM→SIGKILL 阶梯。
|
||||
spawn 会立即返回一个实时句柄。收集模式的读取器接受全流字节偏移量且从不消费,因此独立的读取器不会抢走彼此的增量;管道化的流归调用方所有。终止在每个平台上都以进程树为范围:`terminate()`(唯一的终止动词)执行 SIGTERM→宽限期→SIGKILL 升级,`waitForExit()` 观察整棵进程树,`dispose(graces)` 运行进程外子进程所需的协作式 stdin EOF→SIGTERM→SIGKILL 阶梯。
|
||||
|
||||
```ts type-equiv
|
||||
/**
|
||||
@@ -157,17 +157,11 @@ interface SubprocessHandle {
|
||||
readonly collected: SubprocessCollectedOutputs
|
||||
/** Resolves at process close with exit facts; rejects only for spawn-level failures. */
|
||||
readonly done: Promise<SubprocessOutcome>
|
||||
/**
|
||||
* Send one signal to the process tree, Node-style — no escalation, no
|
||||
* timers. A no-op after the outcome has settled (the pid may be reused).
|
||||
* @param signal - the signal to deliver (default `SIGTERM`; Windows
|
||||
* force-terminates the tree for any value).
|
||||
*/
|
||||
kill(signal?: NodeJS.Signals): void
|
||||
/**
|
||||
* Begin the SIGTERM → `graceMs` → SIGKILL escalation on the process tree
|
||||
* (Windows force-terminates immediately). Idempotent; also triggered by the
|
||||
* spec's abort signal.
|
||||
* (Windows force-terminates immediately) — the seam's only termination
|
||||
* verb. Idempotent, a no-op once the tree is gone (the pid may be reused),
|
||||
* and also triggered by the spec's abort signal.
|
||||
*/
|
||||
terminate(): void
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user