按 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
fix(plugin-dev,runtime): retire the dev analytics stub; /analytics ga…
os-zhuang commentedon 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-storagesearchi18nmetadataworkflowrealtimestubaiautomationnotificationdataauthsecurity.permissionssecurity.rlssecurity.fieldMaskerhandlerReady: 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
fix(core,plugin-dev,runtime): every in-memory fallback and dev stub s…
Merge origin/main into claude/dispatcher-handler-ready-1odd28 — recon…
fix(runtime,metadata-protocol): every dispatcher domain gates on hand…
Merge origin/main into the /storage bridge retirement (#4087)
fix(runtime,hono,plugin-dev): retire the /storage dispatcher bridge —…
fix(runtime,metadata-protocol): the two discovery builders compute th…
fix(runtime): re-point #4058's new /ai/agents assertion at the envelo…
fix(runtime,metadata-protocol): the `data` discovery slot is computed…
fix(plugin-dev,plugin-hono-server): retire the security dev stubs; re…
fix(runtime): the /ai/agents degraded fallback answers in the declare…
fix(runtime)!: the /auth domain answers 501 instead of fabricating a …
fix(runtime): dispatcher `capabilities.search` answers a stated `fals…
fix(runtime): gate the /automation definition writes on manage_metada…