发现于 #5170(统一 kernel:ready 失败语义)的落地期间。#5170 的派发词明确只裁 kernel:ready 一个钩子,其余钩子的语义不在该单范围内,故另开此单,未在 PR 中修。
事实
#5170 落地后,LiteKernel.bootstrap() 的三个钩子分发是这样的:
| 钩子 |
ObjectKernel |
LiteKernel(#5170 之后) |
kernel:ready |
传播,boot 失败 |
传播,boot 失败 ✅ 已统一 |
kernel:bootstrapped |
传播,boot 失败 |
吞 —— 一条 Hook handler failed 日志,boot 继续 |
kernel:listening |
传播,boot 失败 |
吞 —— 同上 |
kernel:shutdown |
传播 |
吞 |
原因与 #5170 同源:kernel.ts 走 this.context.trigger(...)(裸循环、不 catch),lite-kernel.ts 这三处仍走 kernel-base.ts 的 triggerHook(每 handler try/catch,注释 "Continue with other handlers even if one fails")。#5170 只把 kernel:ready 换成了传播版分发器。
为什么这条不只是对称性洁癖
kernel:listening 是 HTTP 服务器真正开监听的锚点。packages/plugins/plugin-hono-server/src/hono-plugin.ts(约 605 行)里:
ctx.hook('kernel:listening', async () => {
const port = this.options.port ?? 3000;
await this.server.listen(port);
...
});
await this.server.listen(port) 没有自己的 try/catch。它 reject 时(特权端口 EACCES、端口回退逻辑本身失败、edge/serverless 宿主上 listen 不可用等),在 LiteKernel 上的结果是:异常被 triggerHook 吞掉 → bootstrap() 继续 → 打印 ✅ Bootstrap complete 并正常 resolve → 进程活着、没有任何 socket 在监听。同一份插件代码在 ObjectKernel 上是 boot 失败。
「端口占用」这一条不算(server.listen 内部有回退到随机端口的逻辑,实测日志 Port 0 is in use, using port 39869 instead),所以触发面比 kernel:ready 窄 —— 但失败模式更难看:健康检查前的那一句「启动成功」是假的。
kernel:bootstrapped 上挂的是 objectql 的 announceOpenMigrationGates、service-automation 的 node-type 审计等 reconcile 类工作,吞掉的后果是审计静默失效,量级较轻。
建议
不是「把 triggerHook 整个改成传播」—— 每个钩子该 fail-soft 还是 fail-loud 是一次独立判断,#5170 已经把这个原则写进 kernel-base.ts 的两个分发器注释里,triggerHookOrThrow 也已就位可直接复用。这条单要裁的是:
- A:
kernel:listening 也改传播(与 ObjectKernel 一致),kernel:bootstrapped 同理;kernel:shutdown 保持吞(停机路径上一个 handler 失败不该阻断其余清理,这个 fail-soft 是有道理的)。
- B:只改
kernel:listening(唯一有具体用户可见失败模式的那个),其余维持现状 + 文档写明。
- C:全维持现状,文档承认差异。
倾向 A:两轴都指向它 —— 长远轴上「同一钩子名在两个内核上给出相反答案」本身就是 declared ≠ enforced(#5170 的立论,原样适用);防 AI 犯错轴上,一个插件作者在 kernel:listening 里写 await listen() 不会想到要自己包 try/catch,而宽容的消费端正是这类错误藏身之处。kernel:shutdown 的 fail-soft 应当保留并在注释里说明理由,让「declared = enforced」是逐钩子写明的判断,而不是默认继承。
落地时应与 #5170 一样,在 lite-kernel.test.ts 上留对称回归用例(#5170 已经 pin 了「其余钩子在 LiteKernel 上保持 fail-soft」,改哪个就改哪条 pin,让改动必须是显式的)。
相关:#5170(kernel:ready,已修)、#5160 / PR #5173(触发这轮排查的 boot 门)。
Filed by the os-dev agent while implementing #5170; 未认领,交 PM 分诊。
发现于 #5170(统一
kernel:ready失败语义)的落地期间。#5170 的派发词明确只裁kernel:ready一个钩子,其余钩子的语义不在该单范围内,故另开此单,未在 PR 中修。事实
#5170 落地后,
LiteKernel.bootstrap()的三个钩子分发是这样的:kernel:readykernel:bootstrappedHook handler failed日志,boot 继续kernel:listeningkernel:shutdown原因与 #5170 同源:
kernel.ts走this.context.trigger(...)(裸循环、不 catch),lite-kernel.ts这三处仍走kernel-base.ts的triggerHook(每 handler try/catch,注释 "Continue with other handlers even if one fails")。#5170 只把kernel:ready换成了传播版分发器。为什么这条不只是对称性洁癖
kernel:listening是 HTTP 服务器真正开监听的锚点。packages/plugins/plugin-hono-server/src/hono-plugin.ts(约 605 行)里:await this.server.listen(port)没有自己的 try/catch。它 reject 时(特权端口 EACCES、端口回退逻辑本身失败、edge/serverless 宿主上 listen 不可用等),在 LiteKernel 上的结果是:异常被triggerHook吞掉 →bootstrap()继续 → 打印✅ Bootstrap complete并正常 resolve → 进程活着、没有任何 socket 在监听。同一份插件代码在 ObjectKernel 上是 boot 失败。「端口占用」这一条不算(
server.listen内部有回退到随机端口的逻辑,实测日志Port 0 is in use, using port 39869 instead),所以触发面比kernel:ready窄 —— 但失败模式更难看:健康检查前的那一句「启动成功」是假的。kernel:bootstrapped上挂的是 objectql 的announceOpenMigrationGates、service-automation 的 node-type 审计等 reconcile 类工作,吞掉的后果是审计静默失效,量级较轻。建议
不是「把
triggerHook整个改成传播」—— 每个钩子该 fail-soft 还是 fail-loud 是一次独立判断,#5170 已经把这个原则写进kernel-base.ts的两个分发器注释里,triggerHookOrThrow也已就位可直接复用。这条单要裁的是:kernel:listening也改传播(与 ObjectKernel 一致),kernel:bootstrapped同理;kernel:shutdown保持吞(停机路径上一个 handler 失败不该阻断其余清理,这个 fail-soft 是有道理的)。kernel:listening(唯一有具体用户可见失败模式的那个),其余维持现状 + 文档写明。倾向 A:两轴都指向它 —— 长远轴上「同一钩子名在两个内核上给出相反答案」本身就是 declared ≠ enforced(#5170 的立论,原样适用);防 AI 犯错轴上,一个插件作者在
kernel:listening里写await listen()不会想到要自己包 try/catch,而宽容的消费端正是这类错误藏身之处。kernel:shutdown的 fail-soft 应当保留并在注释里说明理由,让「declared = enforced」是逐钩子写明的判断,而不是默认继承。落地时应与 #5170 一样,在
lite-kernel.test.ts上留对称回归用例(#5170 已经 pin 了「其余钩子在 LiteKernel 上保持 fail-soft」,改哪个就改哪条 pin,让改动必须是显式的)。相关:#5170(
kernel:ready,已修)、#5160 / PR #5173(触发这轮排查的 boot 门)。Filed by the os-dev agent while implementing #5170; 未认领,交 PM 分诊。