Skip to content

feat(web): releases page - #764

Open
Makisuo wants to merge 4 commits into
mainfrom
worktree-releases-page
Open

feat(web): releases page#764
Makisuo wants to merge 4 commits into
mainfrom
worktree-releases-page

Conversation

@Makisuo

@Makisuo Makisuo commented Sep 5, 2026

Copy link
Copy Markdown
Collaborator

What

A Releases page: what shipped, per service, and whether it changed anything.

  • /releases — filter sidebar (health band, environment, service; all derived client-side from the same rows), plain-text stats, per-service swimlanes with one dot per version at its first-seen time, and a table grouped by commit with day headers, expandable per-service children, and "this version vs. the rest of the service" deltas for error rate and p95.
  • /releases/$commitSha?service=… — commit title and provider link, a comparison table against the other versions of the service in the same window, a versions rail, the service page's four golden-signal charts (with commit markers and a "This version / Other versions" toggle), and three issue lists: new on this version, regressed on it, still occurring.
  • Sidebar entry under Monitor, after Services.

Why

Deploy information already existed as three fragments (the service page's markers and Recent-deploys rail, the services table's deploy cell, and fix verification's version split). None of them answered "what shipped across the fleet, and did it make anything worse?".

How

A release is a commit the moment it starts serving traffic. The service-overview rollups key on vcs.ref.head.revision as CommitSha, so every read here is a GROUP BY over the same hourly/minutely/raw splice the services list uses — no raw traces scan.

  • packages/query-engine: releasesListQuery (one row per service/env/commit with counts, tdigest quantiles and apdex counts), releasesTimelineQuery (org-wide buckets for the swimlanes and the rollout share), releaseErrorFingerprintsQuery (error fingerprints on error_events_by_time keyed on service.version). Tests, catalog fixtures, and the SQL baseline are included; serviceOverviewWindows is now exported for reuse.
  • apps/api: POST /internal/query-engine/releases and /release-detail bundle handlers. The detail runs versions, timeline, two timeseries (commitShas: [sha] and excludedCommitShas: [sha]) and the fingerprints concurrently.
  • apps/web: adapter + atoms, a pure release-model.ts for the derivations (tested), and the two routes. Commit resolution reuses the bulk VCS lookup; the issues panel is one v2 list call keyed on the fingerprints.

Health bands reuse the services table's constants: 50 spans on each side before comparing, 2× error ratio with a 0.5pp floor for "errors up", +25% p95 for "latency up", and under 90% of the latest bucket for "rolling out" (also behind the span floor).

Reviewer notes

  • The health search param is impact, not health: TanStack merges every route's search schema into one union, and /services already owns health with different literals.
  • The comparison is same-window, this version vs. the merged remainder of the service. That handles a canary beside its predecessor; it does not attempt before/after on time.
  • Not in this PR: the coalesce migration so service.version-only (CLI semver) deploys appear, delivered deploy events (GitHub/Cloudflare), and a "fixed by this version" list from verifications.
  • Verified locally against the dev org: list, health filter, row expansion, detail page and the issues bridge all render from real data; every new endpoint returns 200. Scoped typechecks, lint, format and the touched packages' tests pass; the full suite was not run.

View with [code]smith Autofix with [code]smith
Need help on this PR? Tag @codesmith-bot with what you need. Autofix is disabled.

A release is a commit the moment it starts serving traffic. The service
overview rollups already key on `vcs.ref.head.revision`, so the whole page
is a GROUP BY over the splice the services list reads; nothing scans raw
traces.

- query-engine: `releasesListQuery` (one row per service/env/commit),
  `releasesTimelineQuery` (org-wide swimlane buckets) and
  `releaseErrorFingerprintsQuery` (error fingerprints keyed on
  `service.version`), with tests and SQL-catalog fixtures.
- api: `POST /internal/query-engine/releases` and `/release-detail`
  bundles; the detail runs versions, timeline, this-version and
  other-versions timeseries and the fingerprints in one invocation.
- web: `/releases` with a health/environment/service sidebar, per-service
  swimlanes and a commit-grouped table with per-service children and
  "vs. rest of service" deltas; `/releases/$commitSha` with a comparison
  table, versions rail, the service page's four charts with commit
  markers, and new/regressed/ongoing issue lists. Sidebar entry under
  Monitor.

Health bands reuse the services table's deploy thresholds (50 spans on
each side, 2x error ratio, 25% p95). The filter param is `impact`, not
`health`, because the router merges every route's search schema and the
services page already owns `health` with different literals.
# Conflicts:
#	packages/query-engine/src/benchmark/catalog.test.ts
CI's build-web check failed at 651.1 KB gzip against the 650.0 KB cap.
main sits at 648.4 KB, so the page had 1.6 KB to spend and spent 2.7:
0.9 in the domain contract, 0.4 in the atoms module (the adapter), 0.3
in the entry for two routes, and the rest in the route shell.

- Route shell: the search schema spelled its literals inline instead of
  importing the release model (which pulled the whole model into every
  page's first load); the hover-preload loader is gone (loaders stay in
  the shell by config); `releasesQueryInput` moved out of the route file,
  since an export of a route module stays in its shell.
- Contract: dropped fields nothing read — `lastSeen`, the two estimated
  counts, the timeline's per-bucket `errorCount`, the fingerprint
  `lastSeen`, the request `limit` and `excludedNamespaces`.

Lazy-loading the adapter was tried and made things worse: the dynamic
import boundary split `custom-charts` out of the atoms chunk and cost
more than the adapter itself. Local build is now 649.9 KB.
New `releases` organization feature flag (Clerk public metadata key
`releases`, off by default, on for self-hosted like the others). Only the
sidebar and command-palette row are gated; `/releases` and
`/releases/$commitSha` stay reachable by URL.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant