fix(runtime,metadata-protocol): 两个 discovery builder 计算 metadata 槽位,不再互相矛盾 (#4089) - #4114
Merged
Merged
Conversation
…e `metadata` slot instead of contradicting each other (#4089) ADR-0076 D12's rule is that a service self-identifies (`__serviceInfo`) and the discovery builder reports what it declares. #4058 step 1 gave the kernel's `createMemoryMetadata` that marker. It went unread anyway: `metadata` was not in the loop that reads markers, but in a "kernel-provided (always available)" block above it — hardcoded separately in each builder, and the two hardcodes said opposite things about the same slot. - `runtime/http-dispatcher.ts`: permanently `degraded` + "In-memory registry; DB persistence pending". A stack with MetadataPlugin and a real `sys_metadata` table was reported as having no persistence. - `metadata-protocol/protocol.ts`: permanently `available`. A registry that never reaches disk was reported as indistinguishable from the persisted one — the over-declaration #4089 filed. So each builder was wrong for half the stacks, in opposite directions, and a consumer reading `discovery.services.metadata` got a different answer depending on which host answered. Both now read the registered implementation's self-description: `degraded` plus that implementation's own message (which names what is missing and what to install) for the kernel fallback and plugin-dev's dev registry; `available` with no message for MetadataPlugin, or any implementation carrying no marker — the absent-service case included, since the protocol serves `/meta` regardless. `handlerReady: true` is stated unconditionally on both sides, deliberately NOT taken from the self-description: it answers "is `/api/v1/meta` mounted?", and that route is served by the protocol, so a degraded service in the slot does not unmount it. (Contrast `realtime`, where D12 pins `handlerReady: false` because no surface exists at all.) `data` keeps its hardcode: ObjectQL is its only producer, and plugin-dev — which loads ObjectQLPlugin as a child — can never leave its `data` stub in the slot on a host that builds discovery. No routing, gating or dispatch behavior changes; `svcAvailable` and every domain predicate are untouched. Tests pin both directions per builder, the real kernel-boot path end-to-end (ObjectQLPlugin with no MetadataPlugin ⇒ `degraded`), and — the invariant this closes — that the two builders now answer the same service instance identically. Closes #4089 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RfdfmzBq2hdp6gzHaK1S9p
…vailable` describes The `services.metadata` entry is computed now (#4089), so a sample showing `available` is specifically the MetadataPlugin case — a minimal install running the kernel's in-memory fallback reports `degraded` with a message instead. 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:
|
The two docs the drift check flagged that really repeated the removed hardcode (#4089): the client-SDK sample printed `metadata: degraded` unconditionally, and the implementation-status table still said the metadata system was in-memory only with DB persistence pending — `sys_metadata` persistence ships via MetadataPlugin, and the in-memory registry is the fallback for stacks without it. The row keeps its⚠️ : the remaining gaps (multi-instance sync, migration tooling, hot reload) are unrelated to persistence. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RfdfmzBq2hdp6gzHaK1S9p
os-zhuang
marked this pull request as ready for review
July 30, 2026 10:45
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 #4089
背景:#4089 的两半,第一半已由 #4082 落地
issue 提的"需要的决定"选了第 1 条(给五个 core fallback 加
__serviceInfo),这部分在 #4058 step 1 / PR #4082 里已经做完了 ——_fallback: true全部换成__serviceInfo,createMemoryMetadata/Cache/Queue/Job/I18n现在都自称degraded。issue 标题里的 i18n fallback 也随之被通用循环正确读到。剩下的是 issue 最后一段那条核对:
核对结果:这不只是"说法不同",而是 D12 的标记机制在这个槽位上根本没被执行过。
metadata不在读__serviceInfo的那个循环里,而在循环上方一个 "kernel-provided (always available)" 硬编块里,两个 builder 各写一份,且方向相反:runtime/http-dispatcher.ts:997degraded+ "DB persistence pending"sys_metadata真落库的栈被说成没有持久化metadata-protocol/protocol.ts:1407available所以每个 builder 各对一半的栈说错,方向还相反 —— 同一个
discovery.services.metadata,取决于哪个 host 回答,消费者拿到两个不同答案。#4082 刚给createMemoryMetadata加上的标记在两边都没人读,因为两边都没读任何东西。改动
两个 builder 都改成读注册进槽位的实现自己的
__serviceInfo(readServiceSelfInfo):status: 'degraded'+ 该实现自己的 message(写明缺什么、装什么补上),而不是一句通用文案;available,无 message;槽位为空也走这一支(/meta由 protocol 提供,与该服务在不在无关)。handlerReady: true两边都无条件写死,且刻意不从自述里取:它回答的是"/api/v1/meta挂没挂",而这条路由由 protocol 提供 —— 槽位里放一个 degraded 实现不会把它卸掉。(对比realtime:D12 在那里钉handlerReady: false,因为那边根本没有 HTTP surface。)这也正是 #4089 里"门只读handlerReady,修这个标记不会改变任何路由行为"那条判据成立的原因。data保留硬编,附理由:ObjectQL 是它唯一的 producer,而 plugin-dev 总是把ObjectQLPlugin作为子插件加载(dev-plugin.ts:523-525),所以在任何能构建 discovery 的栈里,plugin-dev 的datastub 都进不了槽位。不过这条"真话"来自加载顺序约定而非构造保证,按 Prime Directive #10 单独记为 #4130(含两条候选决定),不在本 PR 扩范围。不涉及任何路由 / 门控 / dispatch 行为:
svcAvailable和各 domain 的 predicate 一行未动。测试
packages/runtime/src/http-dispatcher.test.ts— 两个方向各一条(真实createMemoryMetadata⇒degraded+ 它自己的 message;无标记服务 ⇒available且无残留的 "persistence pending"),外加这次真正要钉住的不变量:同一个服务实例交给两个 builder,status/handlerReady/message/route必须一致。packages/objectql/src/protocol-discovery.test.ts— metadata-protocol 侧同样两个方向。packages/objectql/src/plugin.integration.test.ts— 端到端:真实ObjectKernel+ObjectQLPlugin(不带 MetadataPlugin),内核注入 fallback → protocol shim 走活的服务注册表 → discovery 必须说degraded。自述标记只有在 builder 真去读的时候才有用,这条钉的就是整条链。已跑通:
runtime(65 files)metadata-protocol(17)objectql(80/1183 tests)spec(269)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 里给svcAvailable那条加 core 的内存 fallback 带 _fallback: true 而非 __serviceInfo,readServiceSelfInfo 不识别 —— AppPlugin 注册的 i18n fallback 在 discovery 里报 available #4089 update:标记被尊重,但有两条 entry 从来没走过它。content/docs/kernel/services-checklist.mdx— discovery 样例里那句旧文案就是这次删掉的硬编,改成 fallback 的真实输出并说明它是"计算出来的"。content/docs/api/index.mdx— 样例里的available明确成 MetadataPlugin 的情形(该页把样例框定为 minimal install,而 minimal install 现在报degraded)。content/docs/api/client-sdk.mdx、content/docs/releases/implementation-status.mdx— docs-drift 报的 19 篇里真正重复了旧硬编的两篇:SDK 样例无条件打印metadata: degraded;status 表还写着"in-memory only, DB persistence pending"(sys_metadata持久化早已随 MetadataPlugin 发布)。该行仍保留discovery-metadata-slot-computed.md(两个包 patch;discovery 输出对消费者可见)。