fix(plugin-dev,runtime): retire the dev analytics stub; /analytics gates on handlerReady, not presence (#4000) - #4062
Merged
Conversation
…tes on handlerReady, not presence (#4000) #3891/#3989 retired the degraded analytics shim, making an empty `analytics` slot the honest signal: routes unmounted, request 404, discovery `unavailable`. plugin-dev refilled that slot in dev mode and the retired shape came back one layer down — the dispatcher gates on "is a service registered", so the stub was called like a real engine and its fabricated rows went out as a 200. Dev mode is explicit opt-in and the fake was empty, so this was never the security hole the shim was; the defect is that "the capability is present" meant something different in dev than in production. Both halves are fixed, because deleting the stub alone would leave the gate that let it through: - plugin-dev no longer registers an `analytics` dev stub (`NO_DEV_STUB_SERVICES` — the slot stays empty, not even the shapeless `{ _dev: true }` fallback). Every other dev stub is unchanged. - ADR-0076 D12's third conclusion ("consumers treat only `handlerReady: true` as a real capability") is now executed by the dispatcher, which is a consumer too. `isAnalyticsServiceServeable` is read by the `/analytics` domain, the route-mount gate, and discovery's `routes`/`features` — one predicate, so what is advertised and what is served cannot disagree. `handlerReady`, not `status`, is the test: a `degraded` implementation that genuinely serves keeps serving. - The metadata-protocol discovery builder stops advertising `routes.analytics` (and the service entry's `route`) for a stub, for the same reason. Analytics only: the other stub-backed domains ARE still served by their dispatcher domains, so their presence-gated advertisement stays honest. - `discovery.services.analytics` still reports a registered stub as `status: 'stub', handlerReady: false` — strictly more informative than collapsing it to `unavailable`. To use analytics in dev, install the real engine: @objectstack/service-analytics runs an InMemory strategy. `os serve`/`os dev` are unaffected — `analytics` is in ALWAYS_ON_CAPABILITIES, so the real engine is already installed there. Out of scope, filed as #4058: the other dispatcher domains (storage, automation, i18n, notifications, ai) still gate on presence alone, and plugin-dev's remaining stubs still occupy those slots. Adopting the same rule there is a per-domain call — some of those stubs are working in-memory implementations (`degraded`), not fabricated data (`stub`), and the single `_dev: true` marker cannot tell them apart. ADR-0076 D12 annotated accordingly. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UvcnzFr5XBuvvAd7mosG54
|
The latest updates on your projects. Learn more about Vercel for GitHub. 1 Skipped Deployment
|
Contributor
📓 Docs Drift CheckThis PR changes 4 package(s): 27 hand-written doc(s) reference the affected code and may need an implementation-accuracy re-verification:
|
os-zhuang
marked this pull request as ready for review
July 30, 2026 07:57
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #4000.
采用了 issue 的两条修法都做,因为只做其中一条都留着另一半:删掉 stub 但不改 dispatcher,等于靠"仓库里恰好没有别的 stub"来维持不变量;只改 dispatcher 不删 stub,dev 下的 analytics 就变成"槽位被占住、请求恒 404",比空槽位多一层间接。范围仍严格限在 analytics 一域 —— issue 里点名的"各域统一决定"那部分单独开了 #4058。
改了什么
① 退役 dev analytics stub(
plugin-dev)NO_DEV_STUB_SERVICES明确记下"这个槽位故意留空",注册循环跳过它 —— 注意不能只删DEV_STUB_FACTORIES里的那一行,循环对没有 factory 的服务会退回注册一个无形状的{ _dev: true },那比原来的 stub 更糟。其余 dev stub 一个没动。② dispatcher 执行 ADR-0076 D12 结论第 3 条(
runtime)isAnalyticsServiceServeable(domains/analytics.ts)被三处共用:域判据、路由挂载门、discovery 的routes/features。同一个谓词 ⇒ 广告的和实际服务的不可能对不上,这正是同文件里hasMcp那条注释立的规矩("Same predicate ⇒ same answer")。判据是
handlerReady而不是status:degraded表示"能力打折但真干活",其handlerReady默认true,照常服务;只有自认不接客的才等同空槽位。③ metadata-protocol 的 discovery builder 同步
它是另一个 discovery 生产者,原来只要服务注册就写
routes.analytics。②之后 dispatcher 不再服务 stub,这里若继续广告就是新造一个declared ≠ enforced。同样只针对 analytics:其他 stub 支撑的域现在仍然被各自的 dispatcher 域正常服务,它们按注册与否广告是诚实的。discovery.services.analytics对已注册的 stub 仍报status: 'stub', handlerReady: false—— 比塌缩成unavailable信息量更大,也是 D12 设计这套标记的本意。影响面
os serve/os dev不受影响:analytics在ALWAYS_ON_CAPABILITIES里,真引擎本来就装着(pnpm dev的 showcase dashboard 照常)。受影响的只有自己new DevPlugin()组装、且依赖 stub 应答/analytics/query的嵌入式用法 —— 装@objectstack/service-analytics即可,它有 InMemory 策略,不需要额外数据库。changeset 里写了 FROM → TO。测试
plugin-dev:原来断言 stub 契约的那段改成断言槽位为空 + stub 日志里不含 analytics。runtime:stub 占槽时三条子路由全部handled: false且 stub 的方法一次都没被调用;__serviceInfo两个方向(stub404 /degraded照常服务);挂载门两个方向;discovery 停止广告 route 但仍报 stub。objectql(protocol discovery builder):stub 不广告 route。runtime(913) /plugin-dev(7) /metadata-protocol(99) /rest(505) /objectql(1180)。runtime里datasource-autoconnect.test.ts > makes the federated object queryable…在本容器里失败(ext_note读到 6 行而非 2 行)。与本 PR 无关:在基线 commit 上 stash 掉全部改动后同样失败,回退driver-memory到 #4048 之前也同样失败,而 main 的 CI 是绿的 —— 判断为本地环境问题,故未开 issue,只在此备案。文档
content/docs/kernel/services-checklist.mdx§3 的 callout 补上"dev 模式同样如此"。顺带记录(Prime Directive #10)
#4058 —— dispatcher 其余服务域(storage / automation / i18n / notifications / ai)仍只判槽位占用。里面附了逐域盘点,并给了倾向性结论:这些 stub 该先分成"真能用的内存实现"(
storage/i18n→ 应改标记为degraded)和"编造数据"(ai.chat/automation.execute→ 应走 analytics 这条路),现在所有 stub 共用一个_dev: true被归一化成stub,正是两类分不开的原因。Generated by Claude Code