Files
support_backend/specs/014-full-observability/contracts/metrics-contract.md
T
saqib mirandClaude Sonnet 5 5a0fe9f847 docs(014-full-observability): plan, research, data model, contract, quickstart
Documents the exact hook point chosen for each of the 3 dead observability
primitives (access log, request-duration histogram, tracer provider) and
the 11 named business-health metrics, verified against the real current
code rather than assumed — including a pre-existing SLA-run status data
quality gap surfaced along the way (documented, not fixed here).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-08 10:51:22 +05:30

3.2 KiB

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 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)

# 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.