Skip to content

ADR-0076: remaining objectql/protocol layering work (cross-repo window) #2462

Closed

Description

@os-zhuang

Tracking issue for the remaining work from ADR-0076 (objectql layering / protocol segmentation / honest capabilities).

Done (for context)

Remaining — all gated to a coordinated cross-repo window (framework + cloud + ../objectui), unless noted

  • D10 — distribute protocol implementations to domain packages. Data facade → transport; analytics/feed/realtime impls owned by their existing services (service-analytics/service-messaging/service-realtime); the metadata-management impl stays in @objectstack/metadata-protocol (name retained — OQ#7). Dissolve the central facade. Risk: med; cross-repo.
  • D11 — decompose the transport god-implementation. runtime/http-dispatcher.ts (~3.8k) + rest/rest-server.ts (~5.1k) → thin framework-agnostic port + per-domain normalized handlers (NOT framework-specific routes — keep multi-adapter). Confirm dispatcher↔rest-server overlap; validate multi-adapter by writing a 2nd IHttpServer adapter (thin Workers/Express) to prove the port is free of Hono-isms. Risk: high; cross-repo.
  • D12 — honest capabilities (fixes agents being misled by fake services). Standardize a stub/dev/fallback marker; make http-dispatcher svcAvailable respect it (report status:'stub'/handlerReady:false, never available); add the stub status to the discovery schema; update consumers (agents, console) to trust only real services. Mark the analytics fallback + plugin-dev stubs honestly — do not delete (no /analytics 404). Risk: med; cross-repo (console reads discovery.services). See OQ#11 (marker convention).
  • Step 2 — make the @objectstack/objectql package itself protocol-free. Turn the protocol wiring into a MetadataProtocolPlugin (capability plugin, like plugin-security); update framework boot sites + cloud's ~8 new ObjectQLPlugin() sites to also register it. Risk: high; breaking boot contract; cross-repo (cloud).
  • D7 — engine repo-split (trigger-gated). Only when the cross-package commit ratio of engine.ts/registry.ts (currently 88%) falls to a low, stable level. Track the metric in CI. Then extract on the objectui sibling-link model. Risk: high; deferred.
  • (optional, low-risk, any time) A1.5 — narrow consumers to the new sub-interfaces (e.g. data-only callers → DataProtocol), per the D9 incremental guidance.

Notes

Generated from the ADR-0076 kernel review.

Activity

  1. os-zhuang commented on Jul 16, 2026

    @os-zhuang
    ContributorAuthor

    Concrete D12 instance: discovery advertises a /realtime route that 404s

    Surfaced while auditing HTTP surfaces for #2567 (anonymous-deny). Not a security hole (there is no inbound realtime request path — service-realtime is an in-process pub/sub bus that registers zero HTTP routes, and no dispatcher/plugin mounts /realtime), but it is exactly the D12 "honest capabilities / agents misled by fake services" problem — an advertised capability with no real handler behind it.

    Three reference points:

    1. packages/runtime/src/http-dispatcher.ts getDiscoveryInfo() — sets realtime: hasWebSockets ? \${prefix}/realtime` : undefined, where hasWebSockets = !!realtimeSvc`. Merely registering the in-process service makes discovery advertise an HTTP route that was never mounted.
    2. packages/plugins/plugin-hono-server/src/hono-plugin.ts — the static discovery blob also lists realtime: \${prefix}/realtime``.
    3. packages/metadata-protocol/src/protocol.ts (~L460) — SERVICE_CONFIG.realtime names provider plugin-realtime, a package that does not exist (the real one is packages/services/service-realtime, which registers no routes). The service-realtime README also documents endpoints (POST /realtime/broadcast, ws://…/ws, …) that are unimplemented.

    Fix options (for the D12 window): either implement the realtime HTTP/WS surface (and then it must go through the shouldDenyAnonymous gate + a #2567 conformance ratchet probe), or mark it honestly — gate the discovery advertisement on real handler-readiness (not just !!realtimeSvc) so agents/console don't see a /realtime route that 404s. The client SDK (packages/client/src/realtime-api.ts) may read this field, so a bare removal needs a compat check.

    Filed here rather than as a fresh issue since it's a specific case of D12, not a new workstream.


    Generated by Claude Code

  2. self-assigned this
    on Jul 16, 2026
  3. added a commit that references this issue on Jul 16, 2026
  4. os-zhuang commented on Jul 16, 2026

    @os-zhuang
    ContributorAuthor

    进度更新:D12 framework 侧已合并;D11 两个前置项已完成(PR #3037)

    已合并(#3028,merge commit 9ea3c93) — D12 framework 侧切片:标准化 __serviceInfo 自描述标记(OQ#11 已决)、两个 discovery 构建器尊重标记、analytics fallback 如实报 degraded、/realtime 假路由不再广告(本 issue 评论里的具体实例)、A1.5 REST 层收窄到 DataProtocol & MetadataProtocol、D7 触发指标进 CI(当前读数:近 30 天引擎核心提交 100% 跨包 → D7 继续搁置)。console 侧"只信 handlerReady:true"仍留跨仓库窗口。

    新 PR #3037 — D11 的两个前置项,结论已记入 ADR-0076 OQ#9/OQ#10:

    1. OQ#10 多适配器验证:端口无硬性 Hono 依赖(已证明)。 新增零依赖的裸 node:http 适配器 @objectstack/plugin-node-server + 跨适配器一致性套件:dispatcher 桥 + REST 生成器在两个适配器上真实 socket 行为完全一致(/data CRUD 全流程、:param 路由、404/405、discovery、SSE,40 条断言)。Hono 适配器的 Host 头回填是 Fetch API 产物、非端口泄漏;剩余 Hono 耦合全部在特性探测的 getRawApp() 逃生舱内。窗口期跟进:把 res.write/end(SSE)、getPort() 和 404/405 语义正式写进 IHttpServer 契约。

    2. OQ#9 重叠审计:划清边界,不要合并。 dispatcher 与 rest-server 按域瓜分 API 面,真正双挂载的只有 discovery 和 packages(注册序先到先得);dispatcher 的 /data//meta//ui//notifications//share-links//security 分支在标准启动中未挂载(仅作为 callable 被树外适配器直调时才活)。D11 工作清单:①删除或委托死的 callable 分支(近零爆炸半径,可先行);②packages + discovery 各归一主,消除注册序竞态;③按域抽取 normalized handler 到所属服务包;④环境解析统一到注入的 KernelResolver 缝(退役 rest-server 内建的 hostname→env 平行实现)。

    剩余跨仓库窗口项:D10、D11 拆解本体、Step 2(cloud ~8 处 boot 站点)、console 消费方更新。前置证据已齐,窗口可以排期。


    Generated by Claude Code

  5. added 2 commits that reference this issue on Jul 16, 2026
  6. os-zhuang commented on Jul 17, 2026

    @os-zhuang
    ContributorAuthor

    Progress update — cross-repo window (2026-07-17)

    ✅ Completed since this issue was filed

    🔻 Remaining

    • D11 step ③ — per-domain handlers (framework; largest, key path). Decompose the dispatcher (~3.8k) + rest-server (~5.1k) into a thin registry + per-domain normalized contributions, retiring the createHonoApp catch-all. Unblocks step ① below.
    • D11 step ① — kill/delegate the dead callable branches — RE-SCOPED, blocked on step ③. ⚠️ Cross-repo finding: the dispatcher's /data·/meta·/ui·/notifications·/share-links·/security dispatch() branches are not dead — cloud's two hosts delegate every unmatched ${prefix}/* to dispatch() via the createHonoApp catch-all. There, /share-links is the designed primary surface for per-env kernels (registerShareLinkRoutes:false; host dispatcher serves after kernel swap — documented in cloud artifact-kernel-factory + default-environment-plugins) and /notifications has no other HTTP owner anywhere. Killing therefore waits until the catch-all is retired by step ③. (corrects the original OQ#9 "callable-only" note — see ADR-0076 §OQ#9)
    • D11 step ④ — unify env-resolution on the injected KernelResolver seam (framework; more bounded, relatively independent — a good pre-step ③ cleanup).
    • Step 2 + D10 — make @objectstack/objectql protocol-free and distribute impls (framework + cloud ~8 new ObjectQLPlugin() boot sites). objectql → MetadataProtocol impl; DataProtocol impl → engine-adjacent; dissolve the central facade, transport routes each contract-slice to the owning service. Analytics fallback preserved consciously (no /analytics 404), not deleted. Highest risk; deserves its own coordinated window.
    • D7 — engine repo-split (still trigger-gated on the metric above).

    Dependency shape

    step ③ unblocks step ①; step ④ is independent and can go first; Step 2/D10 (the cloud-touching core goal) is best sequenced after step ③ gives a clean per-domain seam.

    Two PRs merged this session: #3142 (framework D11 step②), objectstack-ai/objectui#2637 (D12 console).


    Generated by Claude Code

  7. 22 remaining items

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