Skip to content

runtime dispatcher's two discovery bodies (.well-known + REST-less {prefix}/discovery fallback) are the machine-read { data } class #9436 ruled on, and sit outside check-route-envelope's scan #9813

Description

@huangyiirene

Found while implementing #9436 (which stays open until its own PR lands; nothing here closes it). Measured on main at 4c260cd. Filed unassigned — recording, not starting.

The gap

packages/runtime/src/dispatcher-plugin.ts serves two discovery bodies in the exact shape #9436 was ruled on, and neither is visible to scripts/check-route-envelope.mjs:

routesitecondition
GET /.well-known/objectstackres.json({ data: await dispatcher.getDiscoveryInfo(prefix) }), ~line 738unconditional — this route is dispatcher-owned in every composition (ADR-0076 D11 / OQ#9)
GET {prefix}/discoverysame body, ~line 765registered only when com.objectstack.rest.api is absent (REST-less compositions; otherwise ceded to REST)

Both are { data } with no success key — one key short of BaseResponseSchema, the same distance the hono adapter's discovery bodies were before #9436's ruling (2026-08-18, option A: envelope them, because machine-read discovery surfaces are the envelope's core constituency and the migration is one key).

Why this is a triage call, not an inherited fix

The #9436 ruling's operative text names only packages/adapters/hono/src/index.ts. The consumer population here is the same — /.well-known/objectstack is the SDK's own discovery fallback probe (connect() in packages/client/src/index.ts reads it), pre-auth, machine-read — so the ruling's reasoning extends naturally, but extending a ruling is not an implementer's move. Note the boundary drawn in #9389 does not cover these either: that exemption is a closed list of SPA-read shell-bootstrap surfaces, and these are not on it.

Reader tolerance is already measured (same sweep as #9436, controls reported there): the SDK unwraps body.data || body; the QA http-adapter discriminates on 'routes' in body; objectui's readers unwrap only when typeof body.success === 'boolean' && 'data' in body. An additive success: true breaks none of them.

Second half: the gate cannot see these sites either way

check-route-envelope.mjs audits three populations: response-envelope write sites, runtime/src/domains/* (returned payload objects), and Hono-context modules listed in PLUGIN_ROUTE_MODULES (judging c.json(...) / ctx.json(...) calls). dispatcher-plugin.ts writes through an express-style res.json(...) on an IHttpServer, which is none of the three — so whichever way the fork is ruled (envelope, or exempt-with-reason), the verdict has no place in the gate table to be recorded until the scan surface grows a fourth population (or these two sites move behind an audited seam).

If the fork is ruled toward the envelope

Two doc pages document the current { data } wrap for the well-known endpoint and would ride the flip: content/docs/api/index.mdx (~line 154) and content/docs/protocol/kernel/http-protocol.mdx (~lines 111–115).

Related: #9436 (the ruling on the hono adapter's mount) · #9389 (the pre-auth exemption boundary, a closed list) · #9559 (rest-server's own envelope conversion; dispatcher-plugin.ts is not in its scope).


Generated by Claude Code

Metadata

Metadata

Assignees

Labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions