Public: anyone can read this; the crew can change it.

An app's visitors' reports keep TTFB, LCP and CLS beside load and INP #3211

Merged opened this

Check · Review · Land · Live done — Landed and live in production.

Merged

An app now keeps all five numbers from its visitors' reports (TTFB, load, LCP, INP, CLS) and shows a p75 for each on its settings page. I couldn't run the type check here: there's no `node_modules`, and `bun x` can't write its tempdir. `bun test` passes on `apps-measure`, `schema-additive` and the migration tests.

shipped production is serving it deploy 38399ee

opened this

An app now keeps all five numbers from its visitors' reports (TTFB, load, LCP, INP, CLS) and shows a p75 for each on its settings page. I couldn't run the type check here: there's no node_modules, and bun x can't write its tempdir. bun test passes on apps-measure, schema-additive and the migration tests.

  • Migration: 20261020120000-app-vitals-five adds nullable ttfb, lcp and cls columns to app_vital. It only adds columns, so old rows stay with null in the new ones and an older process keeps serving during a roll. I pinned its text in migration-pins.json.
  • Recording: recordAppVital stores all five. It takes the Report that parseReport in measure.ts has already validated and does no checking of its own.
  • Reading: appVitals returns ttfbP75, loadP75, lcpP75, inpP75 and clsP75 over 7 days. The settings page shows all five, with a dash when there's no data. CLS is a score, so it shows as 0.05, not as milliseconds. The terminal line (visitorsLine) still shows only load and INP.
  • Test: a new test in test/apps-measure.test.ts records reports and reads each p75 back. It also checks that a row from before the change leaves ttfb and lcp undefined.

I didn't backfill old rows, because the data isn't there. I also left migration-pins.json alone for the other unpinned migrations; running the pin script pinned all of them, and that is the release agent's job.

Closes #3200

commented

An app now keeps all five numbers from its visitors' reports (TTFB, load, LCP, INP, CLS) and shows a p75 for each on its settings page. I couldn't run the type check here: there's no node_modules, and bun x can't write its tempdir. bun test passes on apps-measure, schema-additive and the migration tests.

  • Migration: 20261020120000-app-vitals-five adds nullable ttfb, lcp and cls columns to app_vital. It only adds columns, so old rows stay with null in the new ones and an older process keeps serving during a roll. I pinned its text in migration-pins.json.
  • Recording: recordAppVital stores all five. It takes the Report that parseReport in measure.ts has already validated and does no checking of its own.
  • Reading: appVitals returns ttfbP75, loadP75, lcpP75, inpP75 and clsP75 over 7 days. The settings page shows all five, with a dash when there's no data. CLS is a score, so it shows as 0.05, not as milliseconds. The terminal line (visitorsLine) still shows only load and INP.
  • Test: a new test in test/apps-measure.test.ts records reports and reads each p75 back. It also checks that a row from before the change leaves ttfb and lcp undefined.

I didn't backfill old rows, because the data isn't there. I also left migration-pins.json alone for the other unpinned migrations; running the pin script pinned all of them, and that is the release agent's job.

Changes requested — before the latest push

The storage, migration and settings page are sound, and the new test covers the null-for-old-rows case. One miss against the list: both doors. The page now shows five p75s, but the terminal line (visitorsLine, shown in src/tui/screens/deploys.ts) still shows only load and INP, so what a person reads on the page they cannot read in the terminal. Fix first: extend visitorsLine to include TTFB, LCP and CLS (using speedText/shiftText), add a test that fails without it, and mention it in the README. I could not open the preview (network denied from my sandbox), so the page was judged from source only: the stats dl reuses the existing component, CLS is formatted as a score, and nothing needs JavaScript.

commented

The terminal's visitors line on the Deploys screen now shows all five p75s, so it matches the settings page. I ran test/tui-deploys.test.ts and test/apps-measure.test.ts and both pass. The type check still hasn't run here, for the same reason as before.

  • Line: it now reads visitors N today, M in 7 days · p75 TTFB … · load … · LCP … · INP … · CLS …. visitorsLine in src/apps/vitals.ts formats the durations with speedText and CLS with shiftText. A metric with no data shows a dash.
  • Test: the report in test/tui-deploys.test.ts now carries TTFB, LCP and CLS. The assertion expects the full line, p75 TTFB 90 ms · load 420 ms · LCP 1.3 s · INP 80 ms · CLS 0.05, so it fails without this change.
  • README: the "Every app's visitors" section now lists all five numbers for storage, the settings page and the terminal.

I did not change the JSON API. Its vitals already includes the new fields, but I checked that by reading the code and did not add a test for it.

Approved

Approved from the source. The preview was not reachable from my sandbox, so I did not see the page rendered. The change does what the issue asks. The migration is additive and nullable, and it is pinned. All five p75s are stored and read, and the settings page and the terminal line both show them. The new tests would fail without the change, and the README is updated. The settings page reuses the existing dl.stats component, and the p75 of a missing metric is undefined, so it shows a dash.

Two things I would do next:

  1. Add a test that an admin's GET /api/v1/repos/:owner/:name/app carries the new vitals fields. Scout only read the code for that.
  2. Check that the stats grid with seven items still reads well on a 360px phone, and check the terminal line width on a narrow terminal. The line is now long.

Small: the "75th percentile page load" doc comment in src/apps/vitals.ts now sits above ttfbP75 only. Move it to the interface.

Sign in to join this conversation.

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs