Found while implementing the transport-owned http_requests_total seam for #9650 (PR #9746). Filed unassigned; not graded by the filing seat.
The claim, measured on 8957c72227 (branch head, origin/main merged at 9ff11921a2)
OBSERVABILITY_METRICS_SERVICE ('observability:metrics') has exactly one registrar in the whole repo — ObservabilityServicePlugin.init(), packages/runtime/src/observability/observability-service-plugin.ts:85.
A repo-wide sweep finds no host that registers it:
grep -rn "ObservabilityServicePlugin" --include=*.ts packages/ apps/ examples/
returns only the plugin's own definition, its own test, and three JSDoc mentions pointing readers at it. packages/cli/src/commands/serve.ts never names it.
What serve.ts does instead (:2564): builds one registry from env and threads it into a single consumer.
constobservability=awaitbuildServeObservability();awaitkernel.use(createDispatcherPlugin({ ...,observability, ... }));The transport is constructed 800 lines earlier and gets nothing (serve.ts:1758):
constserverPlugin=newHonoServerPlugin({ port });Consequences, all measured or read on the current ref
Why this is not a one-line change, i.e. why it is filed rather than folded in
buildServeObservability() is called once, deep in the dispatcher block. Calling it a second time at the transport's construction site would build a second exporter — for OS_OBS_EXPORTER=otlp that means two OtlpHttpMetricsRegistry instances with two independent flush timers. So the repair is a choice, not a rewrite:
- hoist/memoize
buildServeObservability() and pass the same block to new HonoServerPlugin({ port, observability }) — narrow, leaves cache/storage still dark; - register
ObservabilityServicePlugin with that block before every consumer and let the documented chain do the rest — one registration, lights up transport, cache and storage together, and matches what the plugin's own JSDoc tells hosts to do ("Register this plugin before any plugin that wants to consume the services").
Option 2 looks right and is the reason this is a card rather than a rider: it changes plugin ordering in serve.ts and turns three currently-silent metric families on at once, which deserves a deliberate decision rather than a side effect of an unrelated PR.
Scope note
packages/cli/src/commands/serve.ts was read only — no edit was made from #9650's dispatch, whose file surface did not include it.
Generated by Claude Code
Found while implementing the transport-owned
http_requests_totalseam for #9650 (PR #9746). Filed unassigned; not graded by the filing seat.The claim, measured on
8957c72227(branch head,origin/mainmerged at9ff11921a2)OBSERVABILITY_METRICS_SERVICE('observability:metrics') has exactly one registrar in the whole repo —ObservabilityServicePlugin.init(),packages/runtime/src/observability/observability-service-plugin.ts:85.A repo-wide sweep finds no host that registers it:
returns only the plugin's own definition, its own test, and three JSDoc mentions pointing readers at it.
packages/cli/src/commands/serve.tsnever names it.What
serve.tsdoes instead (:2564): builds one registry from env and threads it into a single consumer.The transport is constructed 800 lines earlier and gets nothing (
serve.ts:1758):Consequences, all measured or read on the current ref
ObservabilityServicePlugin— explicit option, thenobservability:metrics, then no-op — and lands on the no-op:packages/services/service-cache/src/cache-service-plugin.ts:109packages/services/service-storage/src/storage-service-plugin.ts(same shape)SEMCONVfamilies those emit (cache_lookups_total,storage_operations_total, …) are therefore dark in everyobjectstack servedeployment, howeverOS_OBS_EXPORTERis set.http_requests_totalnever sees auth routes or the REST data API — the two highest-traffic inbound surfaces are outside the only HTTP counter the docs tell operators to monitor #9650 from being discharged end-to-end. The transport seam landing in PR fix(hono-server): emit http_requests_total from the transport so every inbound mount is counted (#9650) #9746 follows the same chain, so with neither the option nor the service present it installs no middleware andhttp_requests_totalkeeps reporting dispatcher routes only. The fix is correct at the seam and inert at the host.Why this is not a one-line change, i.e. why it is filed rather than folded in
buildServeObservability()is called once, deep in the dispatcher block. Calling it a second time at the transport's construction site would build a second exporter — forOS_OBS_EXPORTER=otlpthat means twoOtlpHttpMetricsRegistryinstances with two independent flush timers. So the repair is a choice, not a rewrite:buildServeObservability()and pass the same block tonew HonoServerPlugin({ port, observability })— narrow, leaves cache/storage still dark;ObservabilityServicePluginwith that block before every consumer and let the documented chain do the rest — one registration, lights up transport, cache and storage together, and matches what the plugin's own JSDoc tells hosts to do ("Register this plugin before any plugin that wants to consume the services").Option 2 looks right and is the reason this is a card rather than a rider: it changes plugin ordering in
serve.tsand turns three currently-silent metric families on at once, which deserves a deliberate decision rather than a side effect of an unrelated PR.Scope note
packages/cli/src/commands/serve.tswas read only — no edit was made from #9650's dispatch, whose file surface did not include it.Generated by Claude Code