# Contract: `/metrics` output This feature adds no new HTTP endpoints — `GET /metrics` already exists and its response shape (Prometheus text exposition format) is unchanged. This document is the contract for its **content**: which metric series a consumer (Prometheus, or any scraper) can rely on after this feature ships, replacing the usual per-endpoint request/response contract for a feature with no new routes. ## Guarantees 1. Every metric already exposed today (the default `prom-client` process metrics, and `supporthub_http_request_duration_seconds`) continues to appear, with the same name and label set — FR-010. `supporthub_http_request_duration_seconds` gains real observations where today it has none; its metric name/labels/type do not change. 2. Each of the eleven new series in [data-model.md](../data-model.md#metrics-prometheus-via-prom-client) appears on `/metrics` from process start (a `Counter`/`Histogram` with zero observations still exports its metadata — `# HELP`/`# TYPE` lines — even before its first increment; a consumer's dashboard/alert config can reference it immediately without waiting for the first event). 3. No metric name or label value is derived from unbounded, request-supplied input — every label is one of: a fixed small enum (`outcome`, `resolved_by`, `matched`), a route pattern (bounded by the number of registered routes), a tool name (bounded by the tool registry), an error code or category ID (bounded by admin-configured product data, not raw user text). This is a deliberate constraint, not an incidental one — unbounded label cardinality is a well-known way to make a Prometheus deployment fall over, and every label chosen in data-model.md was checked against this before being finalized. 4. `/health`, `/health/live`, `/health/ready` response shapes are unchanged (FR-010) — this feature does not touch `health.service.ts` or `health.routes.ts`. ## Example (illustrative, not exhaustive) ```text # HELP supporthub_http_request_duration_seconds Duration of HTTP requests in seconds # TYPE supporthub_http_request_duration_seconds histogram supporthub_http_request_duration_seconds_bucket{method="POST",route="/tickets",status_code="201",le="0.1"} 3 supporthub_http_request_duration_seconds_count{method="POST",route="/tickets",status_code="201"} 3 # HELP supporthub_ai_session_outcomes_total Count of AI support sessions by terminal outcome # TYPE supporthub_ai_session_outcomes_total counter supporthub_ai_session_outcomes_total{outcome="resolved"} 12 supporthub_ai_session_outcomes_total{outcome="escalated"} 4 # HELP supporthub_sla_run_outcomes_total Count of SLA runs by outcome # TYPE supporthub_sla_run_outcomes_total counter supporthub_sla_run_outcomes_total{outcome="met"} 9 supporthub_sla_run_outcomes_total{outcome="breached"} 1 ``` ## Verification Integration tests assert against this contract by scraping `GET /metrics` (a real `app.inject` call, real registry) before and after driving each metric's real underlying event through the real API, parsing the specific series' value out of the text response and asserting it moved by exactly the expected amount — never by mocking `prom-client` or the registry itself.