ADR-0076 D12 / #2462 立的规矩是:自认是 stub / dev fake / degraded 的服务,discovery 必须照实报,永远不报 available,免得 AI agent 和 Console 把假能力当真能力。这个机制本身是对的(#3878 里的 analytics shim 就是靠它自报 degraded 的),但它目前有三个洞。
洞 1 —— 标记有三种,诚实层只认两种
packages/spec/src/api/discovery.zod.ts:129 的 readServiceSelfInfo 识别:
svc[SERVICE_SELF_INFO_KEY](__serviceInfo)
svc[SERVICE_DEV_MARKER_KEY](plugin-dev 的 _dev: true)
但 packages/core/src/fallbacks/* 的内存兜底用的是第三种:_fallback: true
(memory-cache.ts:15、memory-i18n.ts:126、memory-job.ts:13、memory-metadata.ts:24、memory-queue.ts:14)。
readServiceSelfInfo 不认识它 → 返回 undefined → http-dispatcher.ts:888 的 svcAvailable 落到默认分支 { status: 'available', handlerReady: true }。
目前真正上线的是 i18n:packages/runtime/src/app-plugin.ts:1173 在 stack 声明了 translations 但没装 I18nServicePlugin 时自动 registerService('i18n', createMemoryI18n())。于是 discovery 对外说"i18n 完全可用",实际是个进程内存实现。
(cache / queue / job 的 _fallback 版本目前只被 plugin-dev 消费,且它在外面套了 _dev: true,所以那三个槽位没有暴露;createMemoryMetadata 我没找到运行时注册点。)
全仓唯一消费 _fallback 的地方是一行日志:app-plugin.ts:1227。
洞 2 —— data 槽位根本没把实例传进去
packages/runtime/src/http-dispatcher.ts:943:
data: svcAvailable(routes.data, 'kernel'),
svcAvailable(route?, provider?, svc?) 的第三参是被检查的对象。这里省略了 → readServiceSelfInfo 从不被调用 → 无论槽里装的是谁(包括 plugin-dev 的 data stub)永远报 available / handlerReady: true。
对比同一段里其它槽位都规规矩矩传了实例(auth/analytics/i18n/cache/...),这看起来是漏了一个参数,不是有意的。
洞 3 —— metadata 硬编码 degraded,方向反了
http-dispatcher.ts:942:
metadata: { enabled: true, status: 'degraded' as const, handlerReady: true,
route: routes.metadata, provider: 'kernel',
message: 'In-memory registry; DB persistence pending' },
不看实例、恒为 degraded。所以装了真正的 MetadataManager(有 DB 持久化)的部署,discovery 依然对外说"内存注册表,DB 持久化待办"。前两个洞是"假的报成真的",这个是"真的报成假的" —— 同一张表里两个方向都不可信。
packages/metadata-protocol/src/protocol.ts:1393-1394 有一处对称问题:metadata / data 被硬编码成 status: 'available', provider: 'objectql'。
为什么值得修
这张表不是装饰:
objectui/packages/react/src/hooks/useDiscovery.ts:190 按 status === 'available' || 'degraded' 决定一个能力是否可用(它正确地拒绝了 stub,是唯一做了区分的消费者);
objectui/.../ConditionalAuthWrapper.tsx:114 直接读 services.auth.status === 'degraded' 改变渲染;
- D12 的原始动机就是 AI agent 会读这张表来决定"我能不能用这个能力"。
一个报 available 的内存 i18n,和 #3878 里那个报 degraded 的 analytics shim 是同一类东西 —— 区别只是前者连自报都没做到。
建议
readServiceSelfInfo 增加对 _fallback: true 的归一化(→ status: 'degraded',配一句 message 指向对应的 ServicePlugin),与现有 _dev 分支同构;
data: 补上第三参;
metadata: 改为读实例(真 manager → available,内存兜底 → degraded),protocol.ts:1393 同步;
- 补一条门:遍历所有已知 fake(3 种标记)逐一注册进槽位,断言 discovery 不把任何一个报成
available —— 这类洞会随着新增兜底不断复发,靠人肉 review 拦不住。
关联:#3878、#3891、ADR-0076 D12、#2462。
核对于 origin/main @ 93f267f。
ADR-0076 D12 / #2462 立的规矩是:自认是 stub / dev fake / degraded 的服务,discovery 必须照实报,永远不报
available,免得 AI agent 和 Console 把假能力当真能力。这个机制本身是对的(#3878 里的 analytics shim 就是靠它自报 degraded 的),但它目前有三个洞。洞 1 —— 标记有三种,诚实层只认两种
packages/spec/src/api/discovery.zod.ts:129的readServiceSelfInfo识别:svc[SERVICE_SELF_INFO_KEY](__serviceInfo)svc[SERVICE_DEV_MARKER_KEY](plugin-dev 的_dev: true)但
packages/core/src/fallbacks/*的内存兜底用的是第三种:_fallback: true(
memory-cache.ts:15、memory-i18n.ts:126、memory-job.ts:13、memory-metadata.ts:24、memory-queue.ts:14)。readServiceSelfInfo不认识它 → 返回undefined→http-dispatcher.ts:888的svcAvailable落到默认分支{ status: 'available', handlerReady: true }。目前真正上线的是 i18n:
packages/runtime/src/app-plugin.ts:1173在 stack 声明了 translations 但没装I18nServicePlugin时自动registerService('i18n', createMemoryI18n())。于是 discovery 对外说"i18n 完全可用",实际是个进程内存实现。(cache / queue / job 的
_fallback版本目前只被 plugin-dev 消费,且它在外面套了_dev: true,所以那三个槽位没有暴露;createMemoryMetadata我没找到运行时注册点。)全仓唯一消费
_fallback的地方是一行日志:app-plugin.ts:1227。洞 2 ——
data槽位根本没把实例传进去packages/runtime/src/http-dispatcher.ts:943:svcAvailable(route?, provider?, svc?)的第三参是被检查的对象。这里省略了 →readServiceSelfInfo从不被调用 → 无论槽里装的是谁(包括 plugin-dev 的 data stub)永远报available / handlerReady: true。对比同一段里其它槽位都规规矩矩传了实例(
auth/analytics/i18n/cache/...),这看起来是漏了一个参数,不是有意的。洞 3 ——
metadata硬编码 degraded,方向反了http-dispatcher.ts:942:不看实例、恒为 degraded。所以装了真正的
MetadataManager(有 DB 持久化)的部署,discovery 依然对外说"内存注册表,DB 持久化待办"。前两个洞是"假的报成真的",这个是"真的报成假的" —— 同一张表里两个方向都不可信。packages/metadata-protocol/src/protocol.ts:1393-1394有一处对称问题:metadata/data被硬编码成status: 'available', provider: 'objectql'。为什么值得修
这张表不是装饰:
objectui/packages/react/src/hooks/useDiscovery.ts:190按status === 'available' || 'degraded'决定一个能力是否可用(它正确地拒绝了stub,是唯一做了区分的消费者);objectui/.../ConditionalAuthWrapper.tsx:114直接读services.auth.status === 'degraded'改变渲染;一个报
available的内存 i18n,和 #3878 里那个报 degraded 的 analytics shim 是同一类东西 —— 区别只是前者连自报都没做到。建议
readServiceSelfInfo增加对_fallback: true的归一化(→status: 'degraded',配一句 message 指向对应的 ServicePlugin),与现有_dev分支同构;data:补上第三参;metadata:改为读实例(真 manager → available,内存兜底 → degraded),protocol.ts:1393同步;available—— 这类洞会随着新增兜底不断复发,靠人肉 review 拦不住。关联:#3878、#3891、ADR-0076 D12、#2462。
核对于
origin/main@ 93f267f。