Files
terdut-server/internal/api
Niklas Ye a602ff3efc Document received_at and resolution_source as public contract
The API reference listed endpoints but never the alert object's fields, so
two of them were load-bearing for clients while being described nowhere.
received_at appeared only in passing, as a stats filter; resolution_source
only inside the stale-expiry prose.

Both carry meaning a client cannot derive on its own. starts_at comes from
Prometheus and never changes for an alert instance, so received_at is the
only signal that a firing alert is still being refreshed — it advances on
every accepted webhook, including the unchanged notifications Alertmanager
re-sends every repeat_interval. resolution_source then says how much to
trust ends_at: under 'alertmanager' it is an end time somebody reported,
but under 'expiry' nothing ever reported one, so it is either a stale
watermark or the sweep timestamp, and only an upper bound.

README gains an alert object field table plus a contract section for each,
including the nullability rules and the advice to tolerate unrecognised
resolution_source values. The field comments in models.Alert now say these
are public API rather than ingest details, and the upsert carries a note at
the received_at line, which is where a regression would be introduced.

Three tests lock the newly documented behaviour, none of which was covered
before — the whole suite passed with the received_at bump deleted from the
upsert, because the expiry tests only ever set that column via SQL:

  - a re-send advances received_at and leaves starts_at alone
  - a discarded out-of-order retry does not count as a heartbeat
  - an expiry resolve preserves a reported ends_at watermark and stamps
    sweep time only when none was known
2026-07-30 09:02:44 +02:00
..
2026-07-28 11:49:39 +02:00
2026-07-28 11:49:39 +02:00
2026-05-22 13:22:45 +02:00
2026-05-20 22:28:23 +02:00
2026-07-28 11:49:39 +02:00