Skip to content

feat(api): cursors on the events and incidents APIs - #640

Merged
fclairamb merged 10 commits into
mainfrom
chore/spec-api-cursors
Oct 23, 2026
Merged

fclairamb merged 10 commits into
mainfrom
chore/spec-api-cursors

Conversation

@fclairamb

@fclairamb fclairamb commented Oct 22, 2026 •

Copy link
Copy Markdown
Owner

Makes the events and incidents list APIs usable by a program that follows alerts (an agent, a script, a bridge) and must resume where it stopped after a restart. Implements specs/done/2026/10/2026-10-22-06-events-and-incidents-cursors.md.

Problem

GET /incidents handed out pagination.cursor but never read it back (// TODO: Parse cursor), so page 2 returned page 1. Events had a cursor but no way to read forward in time.

What changed

  • Incidents cursor works. ListIncidents reads the cursor and filters on (started_at, uid). Incidents are now ordered by started_at DESC, uid DESC, so ties no longer skip or repeat rows. pagination.cursor is now an opaque base64 string. A bare-UID cursor from before is ignored and gives the first page.
  • order=asc on events and incidents (GET /events, /checks/{uid}/events, /incidents/{uid}/events, GET /incidents). The cursor then means "strictly after this one" and is always returned. An empty page returns the cursor it was given. desc stays the default and is unchanged. Any other order value gives a 400.
  • 5 s settle window in asc mode. Rows younger than 5 s are held back so a consumer never skips a late commit. The clock is injectable (WithClock) for tests.
  • Shared cursor code. EncodeCursor / DecodeCursor moved to handlers/base/cursor.go. The events cursor format is unchanged, so old events cursors still decode.
  • MCP list_incidents takes order and cursor and returns the next cursor.
  • Docs. OpenAPI, wiki/api-specification/results-incidents.md, and a new "Follow events from a program" section in web/docs/docs/features/live-updates.md.

Tests

Table-driven tests on SQLite and Postgres: walks over tied timestamps (8 incidents, page size 3, both orders), resume after new rows, empty page, settle window with a fake clock, bare-UID cursor, order=bogus returning 400 on all four endpoints, and the MCP tool. make build-backend lint-back test passes locally. make test-postgres is left to CI.

Decided by autopilot

  • Spec 2026-10-22-06, settle window length: 5 s (spec's recommended answer).
  • Spec 2026-10-22-06, order=asc with no cursor and no since: starts at the oldest event, not at now (spec's recommended answer).

🤖 Generated with Claude Code

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@codecov

codecov Bot commented Oct 22, 2026 •

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 94.91525% with 6 lines in your changes missing coverage. Please review.
✅ Project coverage is 83.70%. Comparing base (da6199c) to head (046c216).
⚠️ Report is 1 commits behind head on main.

Files with missing lines Patch % Lines
server/internal/db/postgres/postgres.go 90.47% 2 Missing ⚠️
server/internal/db/sqlite/sqlite.go 90.47% 2 Missing ⚠️
server/internal/handlers/base/cursor.go 94.44% 1 Missing ⚠️
server/internal/mcp/tools_incidents.go 94.44% 1 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main     #640      +/-   ##
==========================================
+ Coverage   83.67%   83.70%   +0.03%     
==========================================
  Files        1268     1269       +1     
  Lines      129705   129785      +80     
==========================================
+ Hits       108535   108643     +108     
+ Misses      21161    21133      -28     
  Partials        9        9              
Files with missing lines Coverage Δ
server/internal/db/models/event.go 100.00% <ø> (ø)
server/internal/db/models/incident.go 100.00% <ø> (ø)
server/internal/handlers/events/handler.go 99.20% <100.00%> (+0.08%) ⬆️
server/internal/handlers/events/service.go 96.84% <100.00%> (+0.76%) ⬆️
server/internal/handlers/incidents/handler.go 98.50% <100.00%> (+0.02%) ⬆️
server/internal/handlers/incidents/service.go 83.38% <100.00%> (+0.13%) ⬆️
server/internal/handlers/base/cursor.go 94.44% <94.44%> (ø)
server/internal/mcp/tools_incidents.go 86.69% <94.44%> (+5.32%) ⬆️
server/internal/db/postgres/postgres.go 67.63% <90.47%> (+0.11%) ⬆️
server/internal/db/sqlite/sqlite.go 89.83% <90.47%> (+0.14%) ⬆️

... and 12 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

fclairamb and others added 7 commits October 23, 2026 00:05
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Incidents now read their cursor back (keyset on started_at, uid). Events and
incidents accept order=asc: oldest first, cursor means strictly after, the
cursor is always returned, rows younger than 5 s are held back. MCP
list_incidents takes cursor and order. OpenAPI, wiki and docs updated.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Adds handler tests for order=bogus (400) on the events and incidents lists
and an MCP list_incidents test for order=asc, cursor and the invalid order result.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
@fclairamb fclairamb changed the title docs(specs): cursors on the events and incidents APIs feat(api): cursors on the events and incidents APIs Oct 22, 2026
@fclairamb
fclairamb merged commit bbd32b9 into main Oct 23, 2026
35 checks passed
@fclairamb
fclairamb deleted the chore/spec-api-cursors branch October 23, 2026 06:16
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant