fix(runtime,metadata-protocol): data 槽位也改为计算,硬编块就此清空 (#4130) - #4141
Merged
Conversation
… too, closing the hardcoded kernel block (#4130) #4089 made both discovery builders derive the `metadata` entry from the implementation in the slot. `data` was the one entry left that judged itself: `status: 'available'`, `handlerReady: true`, unconditionally, in both builders. That was true — by a convention in a different package. ObjectQL is the slot's only producer (`ctx.registerService('data', this.ql)`), and plugin-dev fills a core slot only when it is empty while always loading `ObjectQLPlugin` as a child, so plugin-dev's `data` stub — `find()` returns `[]`, `insert()` mints an id and stores nothing — never reaches the slot. Neither builder verified any of that. It is the same shape the `metadata` hardcodes had while they were still "roughly right", on the platform's most load-bearing capability, and it breaks the moment a second `data` producer appears or a dev config drops the ObjectQL child. Both builders now read the slot's `__serviceInfo`: - a real engine carries no marker ⇒ `available` + `handlerReady: true`, which is byte-identical to the hardcode it replaces (pinned on a real kernel boot, not just on mocks); - a self-declared stub ⇒ its own `status` / `message`, and `handlerReady: false`, which `readServiceSelfInfo` already defaults for `stub`. `handlerReady` is derived here rather than pinned `true` as it is for `metadata`, because each entry says what its own route does. `/meta` answers from the protocol — down to a last-resort default type list — whatever fills the metadata slot, so a degraded implementation there does not unmount it. `/data` has no such floor: `callData` needs the `protocol` service or an objectql-shaped one and throws 503 without them, and the only way a stub occupies the `data` slot is a stack where ObjectQL never registered, i.e. exactly the stack where `/data` cannot serve. Deliberately NOT done: re-deriving serveability from `protocol`/`objectql` inside the builder. Discovery resolves services UNSCOPED (it has no request context), so on a multi-kernel host that could report the required data capability as `handlerReady: false` while per-request scoped resolution serves it fine — a worse lie than the one being fixed. Nothing routes off this field either: the `data` domain resolves its engine directly, and `isServiceServeable` (#4058 step 2) gates the optional domains only. Tests: both directions per builder, the cross-builder agreement extended to `data` (same instance ⇒ same status / handlerReady / message / route), and the real-boot assertion that a live ObjectQL keeps reporting `available`. Closes #4130 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RfdfmzBq2hdp6gzHaK1S9p
|
The latest updates on your projects. Learn more about Vercel for GitHub. 1 Skipped Deployment
|
Contributor
📓 Docs Drift CheckThis PR changes 2 package(s): 19 hand-written doc(s) reference the affected code and may need an implementation-accuracy re-verification:
|
os-zhuang
marked this pull request as ready for review
July 30, 2026 11:19
This was referenced Jul 30, 2026
os-zhuang
added a commit
that referenced
this pull request
Jul 31, 2026
…ory, not spot checks (#3898) (#4306) The three holes #3898 inventoried are all closed on main — #4082 eliminated the `_fallback` marker by moving every kernel fallback onto the standard `__serviceInfo` descriptor, #4114 made both discovery builders compute the `metadata` slot, #4141 did the same for `data`. What remained was the issue's suggestion 4: a recurrence gate over the known-fake INVENTORY rather than per-slot spot checks. This adds that gate to both discovery builders' suites: iterate CORE_FALLBACK_FACTORIES (the complete fake inventory now that plugin-dev's stub table is retired, ADR-0115), register each product into its own slot, and assert discovery reports it `degraded` — never `available`. Table-driven, so the next fallback added to the table is gated the day it lands. cache/queue/job had no per-slot pin before this: dropping their `svcAvailable(…, svc)` third argument — the exact #4130 regression shape — was test-invisible. Verified the gate bites by simulating that regression (`services.cache.status: expected 'available' to be 'degraded'`). Closes #3898 Co-authored-by: Jack Zhuang <277994282+os-zhuang@users.noreply.github.com> Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #4130 · 接 #4114 / #4089
问题
#4089 让两个 builder 把
metadata槽位改成读实现自己的__serviceInfo,同一个 "kernel-provided" 硬编块里就只剩data还在自己给自己下判决 —— 两边都无条件写status: 'available'+handlerReady: true。这句话目前是真的,但真在别的包里:
data只有一个 producer(ObjectQLPlugin的ctx.registerService('data', this.ql)),而 plugin-dev 只在槽位为空时才填 stub、却总把ObjectQLPlugin当子插件加载,所以它那个datastub(find()恒返回[]、insert()发个 id 什么都不存,DEV_STUB_SELF_INFO里分类为stub)永远进不来。两个 builder 谁也没验证过这条链 —— 这正是metadata那两条硬编在"还大致成立"阶段的同一形状,而且发生在平台最核心的槽位上。改动
两个 builder 都改成读槽位的
__serviceInfo:available+handlerReady: true,与被替换的硬编逐字节相同(不只用 mock 验证,真实内核启动那条也钉了);status/message,handlerReady: false(readServiceSelfInfo对stub的默认值),于是按handlerReady判断的消费者不再把空查询引擎当真引擎。为什么这里
handlerReady是算出来的,而metadata那边钉死true—— 两条 entry 各说自己那条路由的实情:/meta无论槽位里是什么都由 protocol 兜底作答(最后还有一层默认类型表),degraded 实现不会把它卸掉;/data没有这层地板 ——callData需要protocol服务或 objectql 形状的服务,两者都没有就抛 503,而 stub 能占住data槽位的唯一前提恰恰是 ObjectQL 从未注册,也就是/data根本没法服务的那种栈。刻意没做:在 builder 里用
protocol/objectql重新推导 serveability。discovery 解析服务是不带 scope 的(它没有请求上下文),多内核 host 上这会把 required 的 data 能力报成handlerReady: false,而按请求 scope 解析其实服务得很好 —— 那比现在这个谎更糟。这个字段也不驱动任何路由:datadomain 自己直接解析引擎,isServiceServeable(#4058 step 2)只管可选域。测试
packages/runtime/src/http-dispatcher.test.ts— 真引擎(无标记)仍报available/handlerReady: true/provider: kernel/无 message;plugin-dev 那条真实 marker ⇒stub+handlerReady: false+ message 含ObjectQLPlugin;跨 builder 一致性扩到data(同一实例交给两个 builder,status/handlerReady/message/route必须相同)。packages/objectql/src/protocol-discovery.test.ts— metadata-protocol 侧同样两个方向。packages/objectql/src/plugin.integration.test.ts— 真实ObjectKernel+ObjectQLPlugin启动后,同一份 discovery 里data仍是available/handlerReady: true/无 message,即"改成计算"没有把真栈的答案改掉。已跑通:
runtime(65)objectql(81)metadata-protocolspec(270)core(26)rest(35)plugin-hono-server(12)plugin-devmetadatahono全绿;改动文件 eslint 干净,http-dispatcher.ts/protocol.ts无新增类型错误。文档 / changeset
docs/adr/0076-objectql-core-tiering.md— D12 inventory 里 core 的内存 fallback 带 _fallback: true 而非 __serviceInfo,readServiceSelfInfo 不识别 —— AppPlugin 注册的 i18n fallback 在 discovery 里报 available #4089 那条 update 说的是"data保留硬编",现在补 discovery 的data槽位仍是硬编available—— 目前只靠 plugin-dev 的加载顺序约定兜住,不是构造上的保证 #4130 update 说明它也算出来了,并记下handlerReady两个槽位处理不同的理由与那条刻意不做的选择。discovery-data-slot-computed.md(两个包 patch)。Generated by Claude Code