Repository navigation
dispatcher 其余服务域仍只判槽位占用、不读 handlerReady —— #4000 在 analytics 一域落地后剩下的类推面 #4058
Description
Activity
- added a commit that references this issue
on Jul 30, 2026 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。最终分类(判据只有一条:答案是不是真的):
服务 理由 degradedcachequeuejobfile-storagesearchi18nmetadataworkflowrealtime真干活,只是能力打折。storage 真存真取,search 真建索引真匹配 stubaiautomationnotificationdataauthsecurity.permissionssecurity.rlssecurity.fieldMasker答案是编的 handlerReady: false是另一个维度(有没有 HTTP handler 在服务):所有stub+ 没有 HTTP 面的degraded(cache/queue/job/realtime)。两处写 message 时才注意到、值得单独点名的"沉默失败":
memory-job的schedule()永远不会自己触发(没有 timer,只有trigger()真跑),workflow stub 不校验任何状态机(每次 transition 都接受)。两条都写进了各自的message,不再需要读源码才能发现。Step 2 现在可以谈了
分类之后,原来的问题变成两个独立的问题,我的看法:
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 下这些能力到底该不该存在"一起决定,不是纯技术取舍。degraded那一组不需要门 —— 它们真的在服务,门会把能用的东西关掉。它们要的是别的东西:consumer 读到degraded时该有的行为(比如 console 标一个"开发实现"角标),那是 objectui 侧的事。
data/auth/security.*不在 dispatcher 域的讨论范围内(没有对应的/domains/*.ts),但它们是这组里最危险的三个(收下写入不存、一律放行、allow-all)。它们的门在别处 —— 值不值得单开一条,看维护者判断。
Generated by Claude Code
- added 8 commits that reference this issue
on Jul 30, 2026
按 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)/storagedomains/storage.ts:38!storageServicecreateStorageStub/automationdomains/automation.ts:44!automationServicecreateAutomationStubexecute恒success: true,编造/i18ndomains/i18n.ts:41!i18nServicecreateI18nStub/notificationsdomains/notifications.ts:43!servicecreateNotificationStubsuccess/aidomains/ai.ts:34-39!aiService→ 404createAIStubrealtime/search不在此列:dispatcher 没有对应域(realtime 从不广告 HTTP 面,search 的 HTTP 面归 REST 层),它们的 stub 只影响进程内调用者和 discovery 自述,那部分是诚实的。严重性远低于 #3891:全部 dev-only + 显式 opt-in,不像降级 shim 那样落在发行装配上、也不丢 RLS。真正的问题是**"能力存在"在 dev 和生产里含义不同**。
需要的决定
不是一个开关,是按 stub 性质分两类:
ai.chat、automation.execute、notification.send)—— 走 analytics 的路:域 + 挂载门 + discovery 共用handlerReady谓词,stub 槽位 = 空槽位 → 404;并像 analytics 一样直接把 stub 删掉(槽位空着比"占住但被 404"少一层间接)。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。