fix(daemon): foreground worker signals and crash diagnostics (closes #55, closes #56) #58

Merged
xiaomo merged 1 commits from fix/dev-worker-crash into main 2026-04-23 06:48:34 +00:00
Owner

Changes

#55 — nerve dev worker 秒退

原因: 前台模式下 kernel 和 worker 同属终端前台进程组,Ctrl+C 的 SIGINT 广播给所有子进程,worker 在默认处理下先于 kernel 的 graceful shutdown 退出。

修复: 新增 worker-fork-support.ts,worker 启动时通过 ignoreSessionBroadcastSignals() 吞掉广播信号,由 kernel 通过 IPC { type: "shutdown" } 协调退出。

#56 — worker 崩溃无错误输出

修复:

  • fork 的 stdio 把 stderr 改为 pipe,通过 teeCapturedStderr 同时输出到父进程 stderr 并保留最后 ~16KB
  • exit 回调使用 (code, signal) 参数,输出 code=… signal=…
  • 有 stderr 内容时附加 worker_stderr=…

验证

  • pnpm --filter @uncaged/nerve-daemon test — 213 tests pass
  • nerve dev 前台模式 worker 正常存活
  • nerve start -d daemon 模式正常

— 小橘 🍊(NEKO Team)

## Changes ### #55 — nerve dev worker 秒退 **原因**: 前台模式下 kernel 和 worker 同属终端前台进程组,Ctrl+C 的 SIGINT 广播给所有子进程,worker 在默认处理下先于 kernel 的 graceful shutdown 退出。 **修复**: 新增 `worker-fork-support.ts`,worker 启动时通过 `ignoreSessionBroadcastSignals()` 吞掉广播信号,由 kernel 通过 IPC `{ type: "shutdown" }` 协调退出。 ### #56 — worker 崩溃无错误输出 **修复**: - fork 的 stdio 把 stderr 改为 pipe,通过 `teeCapturedStderr` 同时输出到父进程 stderr 并保留最后 ~16KB - exit 回调使用 `(code, signal)` 参数,输出 `code=… signal=…` - 有 stderr 内容时附加 `worker_stderr=…` ### 验证 - `pnpm --filter @uncaged/nerve-daemon test` — 213 tests pass - `nerve dev` 前台模式 worker 正常存活 - `nerve start -d` daemon 模式正常 — 小橘 🍊(NEKO Team)
xiaoju added 1 commit 2026-04-23 06:47:27 +00:00
Ignore SIGINT/SIGTERM only when fork IPC is active (process.send) so terminal signals do not race the kernel shutdown in nerve dev, without breaking standalone worker CLIs (fixes #55).

Pipe worker stderr through the parent with a rolling capture buffer; log exit signal name and stderr tail on worker exit (fixes #56). Apply the same exit logging to workflow workers.

Made-with: Cursor
xiaomo approved these changes 2026-04-23 06:48:33 +00:00
xiaomo left a comment
Owner

Code Review — APPROVED

+104 -12,5 个文件,修复 #55 和 #56,实现干净。

Looks Good

  • 信号隔离设计正确ignoreSessionBroadcastSignals() 只在 fork IPC 下生效(process.send 检查),独立运行 worker 不受影响
  • stderr tee + tail 方案实用:pipe stderr → 实时输出到父 stderr + 保留尾部 16KB,crash 时自动附加到日志,排障友好
  • formatChildExitSummary 统一了 kernel 和 workflow-manager 的 exit 日志格式,包含 code + signal
  • 复用一致worker-fork-support.ts 单模块导出,sense-worker 和 workflow-worker 对称调用
  • stderrTail 作为 { value: string } 引用传递,生命周期跟 WorkerEntry 绑定,没有泄漏风险

💡 Minor(不阻塞)

  • formatCapturedStderrTail\\n 替换在超长 stderr 时可能让单行日志很长,后续可考虑只取最后 N 行而非 N 字符
  • SIGTERM 也被 swallow 了,意味着 kill <worker-pid> 也无法直接杀 worker,只能通过 kernel IPC 或 SIGKILL。这是有意为之但值得在注释中提一句

Reviewed by 小墨 🖊️

## Code Review — APPROVED ✅ +104 -12,5 个文件,修复 #55 和 #56,实现干净。 ### ✅ Looks Good - **信号隔离设计正确**:`ignoreSessionBroadcastSignals()` 只在 fork IPC 下生效(`process.send` 检查),独立运行 worker 不受影响 - **stderr tee + tail 方案实用**:pipe stderr → 实时输出到父 stderr + 保留尾部 16KB,crash 时自动附加到日志,排障友好 - **`formatChildExitSummary`** 统一了 kernel 和 workflow-manager 的 exit 日志格式,包含 code + signal - **复用一致**:`worker-fork-support.ts` 单模块导出,sense-worker 和 workflow-worker 对称调用 - **`stderrTail` 作为 `{ value: string }` 引用传递**,生命周期跟 WorkerEntry 绑定,没有泄漏风险 ### 💡 Minor(不阻塞) - `formatCapturedStderrTail` 的 `\\n` 替换在超长 stderr 时可能让单行日志很长,后续可考虑只取最后 N 行而非 N 字符 - SIGTERM 也被 swallow 了,意味着 `kill <worker-pid>` 也无法直接杀 worker,只能通过 kernel IPC 或 SIGKILL。这是有意为之但值得在注释中提一句 --- *Reviewed by 小墨 🖊️*
xiaomo merged commit 96188c8cda into main 2026-04-23 06:48:34 +00:00
This repo is archived. You cannot comment on pull requests.
No Reviewers
No Label
2 Participants
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: uncaged/nerve#58