Skip to content
SQA Cockpit

ADR-0013 - Observed timing is authoritative; server timing is evidence

ADRsUpdated 3 min readEdit on GitHub ↗

ADR-0013 - Observed timing is authoritative; server timing is evidence #

  • Status: Accepted
  • Date: 2026-05-07
  • Deciders: Natan

Context #

SQA probes need timing for two different jobs:

  1. Operational truth. How long did the probe take from the place SQA

runs? This includes client scheduling, DNS, TCP/TLS, proxies, server work, response transfer, body parsing, and validation.

  1. Diagnosis. If an HTTP service reports its own timing in response

headers, how much time does the server claim it spent internally?

Those are related but not interchangeable. A server can report Server-Timing: app;dur=20 while the SQA probe observes 800 ms because the network path is slow, TLS negotiation is cold, the proxy is slow, or the response body is expensive to read. Conversely, a fast network can hide a slow internal phase unless the server exposes it.

The current runner already measures step duration with performance.now(). The Result envelope has a generic context field that can carry durations, but no standard field names. HTTP components currently ignore timing headers.

Decision #

SQA has two timing categories:

FieldSourceMeaning
observedStartedAtSQA wall clock (Date)ISO timestamp for correlation.
observedCompletedAtSQA wall clock (Date)ISO timestamp for correlation.
observedDurationMsSQA monotonic clock (performance.now())Authoritative elapsed probe duration.
serverTimingHTTP Server-Timing headerOptional per-metric server-reported durations.
serverReportedDurationMsHTTP Server-Timing / known legacy headersOptional summary of server-reported duration.

observedDurationMs is the only authoritative duration for SQA thresholds, baselines, and regressions. It is measured around the full step call, including body read and validation.

observedStartedAt and observedCompletedAt are wall-clock timestamps for correlation with logs, deploys, and incidents. They are not used to compute duration because wall clocks can jump.

serverTiming and serverReportedDurationMs are optional HTTP evidence. They help explain where time went when the server exposes useful headers. They never replace observedDurationMs.

Implementation ownership:

  • src/lib/step.ts measures observed timing and attaches it to any

returned Result.

  • src/lib/result.ts owns the helper that merges timing into a Result

without losing existing context.

  • src/lib/http-timing.ts parses standard Server-Timing and a small

set of legacy response-time headers.

  • HTTP components may include parsed server timing in their own

Result.context.

Consequences #

  • Pro: Every check gets consistent observed timing without each

component implementing a stopwatch.

  • Pro: Timing thresholds compare the experience SQA actually

observed, not a server's partial self-report.

  • Pro: HTTP server timing remains available for diagnosis.
  • Con: A component's own context can contain serverReported*

fields while step() later adds observed* fields, so readers must understand the distinction.

  • Con: Non-HTTP SDK probes only get observed timing unless the SDK

exposes its own server-side timing evidence later.

  • Falsifiability: Revisit if a real threshold needs to alert on

server-side time independently of observed time. That should become a separate threshold over serverReportedDurationMs, not a replacement for observedDurationMs.

See also #

header parser.

Was this page helpful?