Why
深度审计确认(孤儿进程向量,无 zombie——Bun/Node 自动收割,但孤儿成立):
packages/opencode/src/util/process.ts:149-155:Process.stop POSIX 路径 proc.kill()(SIGTERM)后立即返回——不 await proc.exited、无 SIGKILL 升级。调用点 lsp/client.ts:640、lsp/lsp.ts:198-246 的实例 finalizer 因此可能「正常完成但进程还活着」;忽略 SIGTERM 的 language server 永久孤儿(Windows 路径有 taskkill /T /F 对照,POSIX 缺失等价语义)
packages/opencode/src/mcp/index.ts:533-558:MCP state finalizer 对 descendants(pid)(:416-438)只发一轮 SIGTERM,无升级、不等待;closeClient/storeClient(:564-589)与 create 失败回滚(:400-403)只调 client.close(),完全跳过孙进程清理;pgrep -P 快照遍历期间新派生的后代逃逸;Windows 上 descendants 恒为 []
- 放大器:
packages/opencode/src/index.ts:141 CLI finally 无条件 process.exit() 放弃异步 finalizer——在注释声明的取舍(docker MCP 不响应 SIGTERM)之上,前置的异步 kill+await 若未完成,子进程直接孤儿化
Scope
packages/opencode/src/util/process.ts(注意文件头注释:packages/sdk/js/src/process.ts 有同步副本,必须一起改)
packages/opencode/src/mcp/index.ts
packages/opencode/src/index.ts 退出路径(给异步清理一个有界窗口,而非无限等待)
Approach
Process.stop:SIGTERM → 有界等待 exit(如 3-5s)→ SIGKILL 升级 → await exit(与 Windows taskkill /T /F 语义对齐);SDK 副本同步
- MCP:descendants 清理提取为可复用例程,kill 走升级语义并等待;
closeClient/storeClient/create 回滚路径同样覆盖孙进程
index.ts:finally 中对实例清理给有界窗口(超时后仍 process.exit(),保持既有取舍但缩小竞态面)
Acceptance
- 单测(注入假 ChildProcess / 测试专用 server):SIGTERM-忽略的子进程在
Process.stop 升级窗口内被 SIGKILL 终止且 exit 被 await
- MCP 动态 disconnect/reconnect 路径孙进程被清理的回归测试
- 既有 process/mcp/lsp 测试全绿;SDK 副本 diff 一致
Why
深度审计确认(孤儿进程向量,无 zombie——Bun/Node 自动收割,但孤儿成立):
packages/opencode/src/util/process.ts:149-155:Process.stopPOSIX 路径proc.kill()(SIGTERM)后立即返回——不await proc.exited、无 SIGKILL 升级。调用点lsp/client.ts:640、lsp/lsp.ts:198-246的实例 finalizer 因此可能「正常完成但进程还活着」;忽略 SIGTERM 的 language server 永久孤儿(Windows 路径有taskkill /T /F对照,POSIX 缺失等价语义)packages/opencode/src/mcp/index.ts:533-558:MCP state finalizer 对descendants(pid)(:416-438)只发一轮 SIGTERM,无升级、不等待;closeClient/storeClient(:564-589)与create失败回滚(:400-403)只调client.close(),完全跳过孙进程清理;pgrep -P快照遍历期间新派生的后代逃逸;Windows 上descendants恒为[]packages/opencode/src/index.ts:141CLI finally 无条件process.exit()放弃异步 finalizer——在注释声明的取舍(docker MCP 不响应 SIGTERM)之上,前置的异步 kill+await 若未完成,子进程直接孤儿化Scope
packages/opencode/src/util/process.ts(注意文件头注释:packages/sdk/js/src/process.ts有同步副本,必须一起改)packages/opencode/src/mcp/index.tspackages/opencode/src/index.ts退出路径(给异步清理一个有界窗口,而非无限等待)Approach
Process.stop:SIGTERM → 有界等待 exit(如 3-5s)→ SIGKILL 升级 → await exit(与 Windowstaskkill /T /F语义对齐);SDK 副本同步closeClient/storeClient/create回滚路径同样覆盖孙进程index.ts:finally 中对实例清理给有界窗口(超时后仍process.exit(),保持既有取舍但缩小竞态面)Acceptance
Process.stop升级窗口内被 SIGKILL 终止且 exit 被 await