Problem
Two v16.0 regressions found in the #3358 sweep — #3361 (per-request admin-gated Server-Timing) and #3362 (mark-notification-read) — share one root cause: the handler is registered on the runtime HttpDispatcher / createDispatcherPlugin path, but os serve / os dev / os start actually run the hono server (@objectstack/plugin-hono-server), which resolves identity via its own self-contained resolveCtx and mounts only a subset of routes. Handlers on the dispatcher path silently 404 / no-op on the shipped server, even though discovery advertises them (hono-plugin.ts:688 advertises notifications: → 404). /api/v1/mcp returns 501 from the same block (dispatcher-plugin.ts:660). This is a declared ≠ enforced gap for HTTP routes.
Because unit tests exercise the two registration paths in isolation (e.g. server-timing.test.ts calls allowPerfDisclosure() manually "as the dispatcher would"), the integration gap is invisible.
Ask — make route reachability declared === enforced
Why
A metadata/discovery-driven platform must not advertise routes that 404 on its own default server. One reconciliation + one boot-the-real-server test closes an entire class of "works in the dispatcher, dead on os serve" bugs.
Refs: #3358 (sweep), #3361, #3362. Also surfaced in objectstack-ai/objectui#2762.
Problem
Two v16.0 regressions found in the #3358 sweep — #3361 (per-request admin-gated
Server-Timing) and #3362 (mark-notification-read) — share one root cause: the handler is registered on the runtimeHttpDispatcher/createDispatcherPluginpath, butos serve/os dev/os startactually run the hono server (@objectstack/plugin-hono-server), which resolves identity via its own self-containedresolveCtxand mounts only a subset of routes. Handlers on the dispatcher path silently 404 / no-op on the shipped server, even though discovery advertises them (hono-plugin.ts:688advertisesnotifications:→ 404)./api/v1/mcpreturns501from the same block (dispatcher-plugin.ts:660). This is adeclared ≠ enforcedgap for HTTP routes.Because unit tests exercise the two registration paths in isolation (e.g.
server-timing.test.tscallsallowPerfDisclosure()manually "as the dispatcher would"), the integration gap is invisible.Ask — make route reachability
declared === enforced/api/v1/*routes are hono-native vs dispatcher-only, and reconcile the two registration paths so a route registered once is served by the actualos serveentrypoint (or the hono server delegates the dispatcher's routes, as it does for/data).os servemounts it) and asserts every route advertised in/api/v1/discovery(and every dispatcher-registered route) responds — not 404 — for both an anonymous and an admin principal. This gate would have caught v16.0: per-request admin-gated Server-Timing never emits on the hono server (os serve/dev) #3361, v16.0: mark-notification-read 404s on os dev/serve — unread never clears (#3354 not effective on hono server) #3362, and the MCP501./notifications+/notifications/read[/all]) as part of, or ahead of, the reconciliation.Why
A metadata/discovery-driven platform must not advertise routes that 404 on its own default server. One reconciliation + one boot-the-real-server test closes an entire class of "works in the dispatcher, dead on
os serve" bugs.Refs: #3358 (sweep), #3361, #3362. Also surfaced in objectstack-ai/objectui#2762.