Skip to content

fix(core)!: kernel:bootstrapped / kernel:listening 抛错在 LiteKernel 上也失败 boot(#5257) - #5275

Merged
os-zhuang merged 1 commit into
mainfrom
claude/issue-5257-listening-bootstrapped-propagate
Aug 4, 2026
Merged

fix(core)!: kernel:bootstrapped / kernel:listening 抛错在 LiteKernel 上也失败 boot(#5257)#5275
os-zhuang merged 1 commit into
mainfrom
claude/issue-5257-listening-bootstrapped-propagate

Conversation

@os-zhuang

Copy link
Copy Markdown
Contributor

Fixes #5257

按 13:12Z 的 PM 裁定取 A,范围严格照单:两个 boot 路径钩子改传播,kernel:shutdown 保留 fail-soft 并把理由写在分发点。

改了什么

packages/core/src/lite-kernel.ts —— kernel:bootstrappedkernel:listening 从隔离式 triggerHook 换成传播式 triggerHookOrThrow(#5170 / PR #5258 已落地的分发器,直接复用),并和 kernel:ready 一起收进同一个 try:一个 handler 抛错 ⇒ 该钩子后续 handler 不再跑、后面的 boot 钩子完全不触发、原始错误不包装地抛给调用方、state = 'stopped'不打印「✅ Bootstrap complete」。这与 ObjectKernel.bootstrap()context.trigger(裸 await 循环)完全同形,#5258kernel:ready 立的就是这个形状。

kernel:shutdown 保持 triggerHook 不变。分发点现在写着为什么:停机路径上排在失败 handler 后面的,正是其余订阅者的清理和逆序的 destroy() —— 冲刷缓冲、关连接、放锁。中断它等于把一个坏 handler 放大成资源泄漏和未落盘的写。这是逐钩子写下来的判断,不是继承来的默认值,所以两个分发器是按钩子挑的,而不是整体换掉。

packages/plugins/plugin-hono-server/** 一个字没动 —— 传播本身就让它现有的代码是对的。

为什么这条不只是对称性洁癖

HonoServerPluginkernel:listening 里开监听套接字:await this.server.listen(port),故意没有自己的 try/catch。它 reject 时(特权端口 EACCES、端口回退逻辑本身失败、edge/serverless 宿主上 listen 根本不可用),在 LiteKernel 上异常被吞 → bootstrap() 正常 resolve → 打印「✅ Bootstrap complete」→ 进程活着,没有任何 socket 在监听。同一份插件代码在 ObjectKernel 上是 boot 失败。第一个发现不对的是紧随其后的健康检查,而它已经被告知启动成功了。

「端口占用」从来触发不了这条(server.listen 内部会回退到随机端口),这正是它一直没被发现的原因。

kernel:bootstrapped 上挂的是 reconcile / 审计类工作(objectql 的 announceOpenMigrationGates、service-automation 的 node-type 与触发器绑定审计、plugin-sharing 的开机回填),吞掉是同一个谎的安静版本:审计静默失效。

证据闸(枚举 + 跑套件)

非测试的 kernel:bootstrapped / kernel:listening 订阅者全表,以及各自在健康 boot 下会不会抛:

订阅点 钩子 健康 boot 会抛吗
packages/objectql/src/plugin.ts:376 bootstrapped 否 —— announceOpenMigrationGates() 自带 try { … } catch {},注释原文「An advisory must never be the reason a boot fails」
packages/plugins/plugin-sharing/src/sharing-plugin.ts:661 bootstrapped 否 —— 五个回填/清扫各自包了 try/catch,失败只 warn
packages/plugins/plugin-pinyin-search/src/pinyin-search-plugin.ts:82 bootstrapped 否 —— 整个 handler 体在 try/catch 内,且 if (!engine) return
packages/services/service-automation/src/plugin.ts:843 bootstrapped 否 —— if (!this.engine) return 之后全是内存态读取(sealNodeTypeVocabulary / getTriggerBindingAudit / getFlowRuntimeStates)+ 日志
packages/plugins/plugin-hono-server/src/hono-plugin.ts:605 listening —— 这正是本单要传播的那一条
examples/app-showcase/src/security/{bind-position-sets,seed-approval-demo}.ts bootstrapped 否(健康 boot 下);且 showcase 走 ObjectKernel,那边本来就是传播

没有任何现存测试依赖这个吞:不需要 needs_decision#5258 的爆炸半径集合原样重跑,外加三个直接订阅这两个钩子的插件包,全绿:

结果
@objectstack/core 27 files / 426 tests passed(main 上是 422,+4 为本 PR 新增)
@objectstack/runtime 89 files / 1313 passed
@objectstack/client 17 files / 222 passed
@objectstack/service-automation 55 files / 665 passed
@objectstack/http-conformance 2 files / 46 passed
connector-{rest,mcp,slack,openapi} 16/23/8/32 passed
plugin-hono-server 14 files / 164 passed
plugin-sharing 13 files / 347 passed
plugin-pinyin-search 2 files / 14 passed

闸门:check:type-check-coveragecheck:doc-authoringcheck:release-notescheck:adr-anchorscheck:nul-bytescheck:startup-registry-verdictcheck:durability-log-levelcheck:init-service-contract 全部 PASS。

@objectstack/core 没有 typecheck script(#4311 的 DEBT 台账,冻结 91)。实测:main 基线 tsc --noEmit = 91,本分支 = 95,+4 全是两个测试文件里既有的那一类 TS7006「Parameter 'ctx' implicitly has an 'any' type」噪声(根因是 ./types 相对导入缺 .js,由 #4311 统一处理),与新增用例数一一对应。台账不按数字设闸,故不改台账;在此明说以免日后重测时被当成新增漂移。

pin 的显式翻面

#5258 留下的 pin 叫 keeps fail-soft dispatch for hooks other than kernel:ready (#5170),一条断言同时盖住 bootstrapped / listening / shutdown 三个钩子 —— 因为 #5170 的派发词只裁 kernel:ready。本 PR 把它拆成三条现在为真的用例,逐钩子,让翻面在 diff 里看得见,而不是悄悄放宽:

  • fails the boot when a kernel:bootstrapped handler throws (#5257)
  • fails the boot — and never logs "Bootstrap complete" — when a kernel:listening handler throws (#5257)
  • keeps fail-soft dispatch for kernel:shutdown — a failing handler must not block the remaining cleanup (#5257)(旧 pin 幸存的那一半,连同理由一起附到它唯一适用的那个钩子上;顺带断言后面的 destroy() 也确实跑到了)

前两条在 kernel.test.ts 里有对称的 ObjectKernel 双胞胎(照 #5258 的形状)。ObjectKernel 本来就是这个行为,加用例是因为:#5170/#5257 关掉的 bug 就是「一个钩子名在两个内核上是两个相反语义」,只有成对的用例才按得住 —— 任一内核回归都由一条具名用例接住,而不是靠另一条还绿着去推断。

kernel:listening 那条额外断言「Bootstrap complete 从未被打印」:bootstrap() reject 只是契约的一半,另一半是出门时没有任何东西宣告成功

范围外发现(已单开,未认领)

文档

content/docs/kernel/events.mdx —— #5258 加的那句诚实说明(「其余钩子两内核仍不同」)按新事实改写:三个 boot 路径钩子在两个内核上一致传播、且不会打印成功行;kernel:ready 仍是放 boot 断言的地方(注册表到那时才填完);kernel:shutdown刻意的例外并说明理由,同时诚实标注 ObjectKernel 停机路径尚未对齐(指向 #5274)。

Changeset:.changeset/kernel-boot-hook-failure-propagation.md,minor(与 #5258 同级),受众提示照 #5258 形状(vitest / serverless / edge 宿主中 bootstrapped/listening handler 会抛的那些),外加本单的具体标题句 —— listen() 失败不会再换来一句假的「Bootstrap complete」


🤖 Generated with Claude Code

https://claude.ai/code/session_01Pbu27iNUfQCHeuS551Rqo7


Generated by Claude Code

… fails the boot on LiteKernel too (#5257)

#5170 (PR #5258) unified `kernel:ready` across the two kernels and deliberately
ruled that hook ONLY, pinning the rest as "still fail-soft on LiteKernel" so
widening it would have to be a deliberate change. This is that change, for the
two hooks that are still on the BOOT path.

`kernel:bootstrapped` and `kernel:listening` now use the propagating dispatcher
(`triggerHookOrThrow`) on LiteKernel, in the shape #5258 established: remaining
handlers skipped, later boot hooks never fired, original error unwrapped,
`state = 'stopped'`, and no "✅ Bootstrap complete".

The failure this removes is the ugliest one available. HonoServerPlugin opens
its socket inside a `kernel:listening` handler — `await server.listen(port)`,
with no try/catch of its own, deliberately. When that rejected on LiteKernel
(EACCES on a privileged port, a failure inside the port-fallback logic, an
edge/serverless host where listen is unavailable) the throw was swallowed,
`bootstrap()` resolved, and the process printed "✅ Bootstrap complete" with
nothing listening — the health check after it was the first thing to notice, and
it had already been told startup succeeded. The same plugin code on ObjectKernel
failed the boot. Plain port-in-use never triggered it (server.listen falls back
to a random port internally), which is why it stayed invisible.

`kernel:shutdown` KEEPS fail-soft dispatch, now as an explicit per-hook
judgement recorded at the dispatch site rather than an inherited default: on the
teardown path the handlers queued behind a failing one — and the reverse-order
destroy() pass after them — are what flush buffers and release resources.

#5258's pin "keeps fail-soft dispatch for hooks other than kernel:ready" covered
all three hooks in one assertion; it is split here into the three tests that are
now true, per hook, so the flip is visible in the diff. Symmetric regressions
added on BOTH kernels for bootstrapped and listening (incl. asserting the
success line is never logged).

Evidence: every non-test `kernel:bootstrapped` subscriber already guards itself
(objectql's announceOpenMigrationGates catches internally — "an advisory must
never be the reason a boot fails"; plugin-sharing wraps each of its five passes;
plugin-pinyin-search wraps its backfill; service-automation's audit is
in-memory reads behind an `if (!this.engine) return`). Suites: core 426,
runtime 1313, client 222, http-conformance 46, service-automation 665,
connector-{rest,mcp,slack,openapi} 79, plugin-hono-server 164, plugin-sharing
347, plugin-pinyin-search 14 — all pass.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Pbu27iNUfQCHeuS551Rqo7
@vercel

vercel Bot commented Aug 4, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
Project Deployment Actions Updated (UTC)
objectstack Ignored Ignored Aug 4, 2026 2:05pm

Request Review

@github-actions github-actions Bot added documentation Improvements or additions to documentation tests tooling size/m labels Aug 4, 2026
@github-actions

github-actions Bot commented Aug 4, 2026

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 1 package(s): @objectstack/core.

24 hand-written doc(s) reference the affected code and may need an implementation-accuracy re-verification:

  • content/docs/ai/actions-as-tools.mdx (via @objectstack/core)
  • content/docs/ai/knowledge-rag.mdx (via @objectstack/core)
  • content/docs/ai/natural-language-queries.mdx (via @objectstack/core)
  • content/docs/automation/webhooks.mdx (via @objectstack/core)
  • content/docs/concepts/north-star.mdx (via packages/core)
  • content/docs/deployment/migration-from-objectql.mdx (via @objectstack/core)
  • content/docs/kernel/contracts/index.mdx (via @objectstack/core)
  • content/docs/kernel/runtime-services/examples.mdx (via @objectstack/core)
  • content/docs/kernel/services-checklist.mdx (via @objectstack/core)
  • content/docs/kernel/services.mdx (via @objectstack/core)
  • content/docs/permissions/authentication.mdx (via @objectstack/core)
  • content/docs/permissions/authorization.mdx (via packages/core)
  • content/docs/plugins/anatomy.mdx (via @objectstack/core)
  • content/docs/plugins/development.mdx (via @objectstack/core)
  • content/docs/plugins/index.mdx (via @objectstack/core)
  • content/docs/plugins/packages.mdx (via @objectstack/core)
  • content/docs/protocol/kernel/config-resolution.mdx (via @objectstack/core)
  • content/docs/protocol/kernel/index.mdx (via @objectstack/core)
  • content/docs/protocol/kernel/lifecycle.mdx (via @objectstack/core)
  • content/docs/protocol/kernel/plugin-spec.mdx (via @objectstack/core)
  • content/docs/releases/implementation-status.mdx (via @objectstack/core)
  • content/docs/releases/v12.mdx (via @objectstack/core)
  • content/docs/releases/v15.mdx (via @objectstack/core)
  • content/docs/releases/v17.mdx (via @objectstack/core)

Advisory only. To re-verify, run the docs-accuracy-audit workflow scoped to these files:
node scripts/docs-audit/affected-docs.mjs origin/main → pass the list as args.docs.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation size/m tests tooling

Projects

None yet

2 participants