Skip to content

spec: give IHttpServer a response-observing hook so HTTP metrics are transport-agnostic instead of Hono-only #9835

Description

@huangyiirene

Filed as directed by the maintainer's 2026-08-18 ruling on #9650, which chose the transport-owned seam (Option A) and named this its principled successor, to be its own packages/spec card and explicitly not folded into that fix.

Verbatim from the ruling:

Option B (IHttpServer response-observing contract hook) is the principled successor: file it as its own spec card, ⛔ not folded into this fix.

Documented expectation: a transport that does not implement the seam reports no HTTP metrics. State this plainly in the docs/PR body rather than letting it read as coverage.

Filed unassigned; not graded by the filing seat.

The gap this closes

PR #9746 emits http_requests_total{method,route,status} from the Hono adapter, as a raw-app middleware installed at the end of HonoServerPlugin.init(). That reaches every inbound request on that transport, including the getRawApp() mounts no IHttpServer-level wrapper can see — measured, at least 14 surfaces in two classes on #9650.

What it cannot reach is another transport. packages/qa/http-conformance/src/adapter.ts:37-41 registers http.server and deliberately does not implement getRawApp(); it has no equivalent seam, so it emits nothing. Zero there means "not instrumented", never "no traffic" — and that is precisely the failure mode #9650 is about, one level up. The ruling accepts it as a documented expectation for now; this card is how it stops being one.

Why the existing use() seam is not the answer — measured, not argued

packages/runtime/src/http-metrics-inbound-coverage.hono.integration.test.ts pins it: the framework-agnostic use() middleware chain runs to completion and only then returns Hono's next(), so a middleware there sees method, path, query and headers and has no response. It cannot carry the status label, which is the label the whole operator guidance (5xx rate) is keyed on. Body is deliberately absent there too, for the reasons the use() contract states.

So the contract needs something the current one cannot express: an observation point that runs after the response exists.

Sketch, for the spec seat to accept or replace

An afterResponse style registration on IHttpServer — something an adapter invokes once per request with { method, routePattern, status, elapsedMs } — plus:

Not urgent, but not free to defer indefinitely

The label shape ships with #9650. Once dashboards are wired against it, any adapter that later joins with a different label convention is a breaking change to the operator's queries rather than to any code, which is the class of change nothing in CI can catch.


Generated by Claude Code

Activity

  1. os-warren commented on Aug 19, 2026

    @os-warren
    Collaborator

    Triage: lands in domain:spec (packages/spec contract addition — IHttpServer response-observing hook). Feature, pm:queue, but no decision needed: the maintainer's 2026-08-18 ruling on #9650 already named and authorized this card verbatim ("Option B ... is the principled successor: file it as its own spec card"). Dispatch at contract-review tier per Clause-② (this is a packages/spec contract-surface change) — flagging for the dispatching seat, not re-deciding here.


    Generated by Claude Code


    Generated by Claude Code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions