Skip to content

ADR-0076 D12 诚实能力层的三个洞:_fallback 标记不被识别、data 槽位不传实例、metadata 硬编码 degraded #3898

Closed

Description

@os-zhuang

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 是同一类东西 —— 区别只是前者连自报都没做到。

建议

  1. readServiceSelfInfo 增加对 _fallback: true 的归一化(→ status: 'degraded',配一句 message 指向对应的 ServicePlugin),与现有 _dev 分支同构;
  2. data: 补上第三参;
  3. metadata: 改为读实例(真 manager → available,内存兜底 → degraded),protocol.ts:1393 同步;
  4. 补一条门:遍历所有已知 fake(3 种标记)逐一注册进槽位,断言 discovery 不把任何一个报成 available —— 这类洞会随着新增兜底不断复发,靠人肉 review 拦不住。

关联:#3878、#3891、ADR-0076 D12、#2462。

核对于 origin/main @ 93f267f。

Activity

  1. self-assigned this
    on Jul 31, 2026
  2. os-zhuang commented on Jul 31, 2026

    @os-zhuang
    ContributorAuthor

    核对当前 main,三个洞已全部由已合并的 PR 修复,处置如下:

    剩余的建议 4(防复发门)由 #4306 补齐:遍历 CORE_FALLBACK_FACTORIES(stub 表退役、_fallback 消灭后,这就是完整的已知 fake 清单)逐槽注册,断言两个 discovery builder 都绝不报 available。补门时发现 cache/queue/job 三个槽位此前在 discovery 层没有任何 pin —— 删掉第三参不会有测试变红,门的负向验证证实了这一点。该 PR 合并后本 issue 关闭。

  3. os-zhuang commented on Jul 31, 2026

    @os-zhuang
    ContributorAuthor

    收尾核对时又划出两条同类残留,已按 Prime Directive #10 分别记录:#4318(cache/queue/job 槽位声明了三条无人挂载的 route)、#4319(_dev 遗留标记生产者已清零,可按 enforce-or-remove 收敛到唯一的 __serviceInfo)。

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

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions