Skip to content

dispatcher 其余服务域仍只判槽位占用、不读 handlerReady —— #4000 在 analytics 一域落地后剩下的类推面 #4058

Description

@os-zhuang

按 Prime Directive #10 记录,#4000 修复过程中划出的域外部分。#4000 的"修法 2"(dispatcher 域尊重 handlerReady: false)只在 analytics 一域落地,因为原 issue 已经写明"这条更通用,但改动面大——各域都要统一决定是否采纳"。这条就是那个待决定的面。

现象

ADR-0076 D12 结论第 3 条要求 consumer "只把 handlerReady: true / status: 'available' 当真能力"。discovery 从 #3028 起就执行了这条;dispatcher 作为 consumer 一直没执行 —— 各服务域只判 getService() / resolveService() 的真假,槽位被占住就当真实现调用,stub 返回的编造数据以 200 发回。

#4000 把 analytics 一域改成读 handlerReady(isAnalyticsServiceServeable,域判据 / 路由挂载门 / discovery 的 routes+features 共用同一个谓词),并删掉了 plugin-dev 的 analytics stub。其余域没动。

盘点(核对于 b3a2318)

域 解析处 现判据 dev 下占槽的 stub stub 返回什么
/storage domains/storage.ts:38 !storageService createStorageStub 内存文件,真能用
/automation domains/automation.ts:44 !automationService createAutomationStub execute 恒 success: true,编造
/i18n domains/i18n.ts:41 !i18nService createI18nStub 内存翻译表,真能用
/notifications domains/notifications.ts:43 !service createNotificationStub 只 push 进数组,声称 success
/ai domains/ai.ts:34-39 !aiService → 404 createAIStub 假回答 —— D12 记的"已经误导过 agent"就是它

realtime / search 不在此列:dispatcher 没有对应域(realtime 从不广告 HTTP 面,search 的 HTTP 面归 REST 层),它们的 stub 只影响进程内调用者和 discovery 自述,那部分是诚实的。

严重性远低于 #3891:全部 dev-only + 显式 opt-in,不像降级 shim 那样落在发行装配上、也不丢 RLS。真正的问题是**"能力存在"在 dev 和生产里含义不同**。

需要的决定

不是一个开关,是按 stub 性质分两类:

  1. 返回编造数据的(ai.chat、automation.execute、notification.send)—— 走 analytics 的路:域 + 挂载门 + discovery 共用 handlerReady 谓词,stub 槽位 = 空槽位 → 404;并像 analytics 一样直接把 stub 删掉(槽位空着比"占住但被 404"少一层间接)。
  2. 真能用的内存实现(storage、i18n,以及没有 HTTP 面的 cache/queue/job)—— 这些不是"假数据"而是"功能受限但真干活",按 D12 的定义正好是 degraded(handlerReady 默认 true)而不是 stub。这类该改的是标记(_dev: true → __serviceInfo: { status: 'degraded', message: … }),不是门。

倾向先做分类再动代码:现在所有 plugin-dev stub 共用一个 _dev: true,被归一化成 { status: 'stub', handlerReady: false },这个"一刀切"本身就是上面两类分不开的原因。

关联:#4000、#3891、#3989、ADR-0076 D12。

Activity

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

    @os-zhuang
    ContributorAuthor

    Step 1(分类)已在 #4082 落地 —— 只改自述标记,不动任何门。

    盘的过程中多出一条:这条 issue 原来的清单漏了内核自己的 fallback(createMemoryCache / Queue / Job / I18n / Metadata,由 ObjectKernel 自动注册)。它们带的是 _fallback: true,而这个 key 没有任何 consumer 认识(readServiceSelfInfo 只认 __serviceInfo 和 _dev),于是两个 discovery builder 都把它们报成完整可用的 available。比 plugin-dev 那半边严重:那半边至少报的是 stub。已一并改成 degraded + 自述 message。

    最终分类(判据只有一条:答案是不是真的):

    服务 理由
    degraded cache queue job file-storage search i18n metadata workflow realtime 真干活,只是能力打折。storage 真存真取,search 真建索引真匹配
    stub ai automation notification data auth security.permissions security.rls security.fieldMasker 答案是编的

    handlerReady: false 是另一个维度(有没有 HTTP handler 在服务):所有 stub + 没有 HTTP 面的 degraded(cache/queue/job/realtime)。

    两处写 message 时才注意到、值得单独点名的"沉默失败":memory-job 的 schedule() 永远不会自己触发(没有 timer,只有 trigger() 真跑),workflow stub 不校验任何状态机(每次 transition 都接受)。两条都写进了各自的 message,不再需要读源码才能发现。

    Step 2 现在可以谈了

    分类之后,原来的问题变成两个独立的问题,我的看法:

    1. stub 那一组该不该像 analytics 一样加门? —— ai / automation / notification 三个有 dispatcher 域的,倾向"是":它们返回编造答案,和 plugin-dev 的 analytics dev stub 仍会填满槽位,dispatcher 不检查 handlerReady —— #3891 退役 shim 后同形状的最后一处残留(dev-only) #4000 修掉的形状完全一致。但和 analytics 有一个关键差别——analytics 有真引擎可装(@objectstack/service-analytics),而 ai/automation 在 dev 下没有等价的"装真的就行"路径,加门等于 dev 模式直接失去这些 API。所以这一步应该和"dev 下这些能力到底该不该存在"一起决定,不是纯技术取舍。
    2. degraded 那一组不需要门 —— 它们真的在服务,门会把能用的东西关掉。它们要的是别的东西:consumer 读到 degraded 时该有的行为(比如 console 标一个"开发实现"角标),那是 objectui 侧的事。

    data / auth / security.* 不在 dispatcher 域的讨论范围内(没有对应的 /domains/*.ts),但它们是这组里最危险的三个(收下写入不存、一律放行、allow-all)。它们的门在别处 —— 值不值得单开一条,看维护者判断。


    Generated by Claude Code

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