Production Core Web Vitals Review
Lighthouse measures one page load, once, under fixed lab conditions. Real
users load the same page on a huge range of devices, networks, and cache
states. This skill closes that gap: pull real-user field data and compare
it against the lab numbers to find pages that look fine in a lab report
but are actually slow (or vice versa) for real visitors.
Works on any public site — CrUX needs no setup, just enough real traffic
for Google to have collected a stable sample for that origin/URL.
When not to use this
- No lab baseline exists yet → run the Lighthouse pass first (this
project's own baseline step, or
npx lighthouse/lhci autorun); this
skill needs something to compare field data against.
- Setting up RUM/analytics that doesn't exist yet → that's
implementation work, not an audit. Report "no field data available" as
the finding and stop there.
- Synthetic load testing (how does the site behave under N concurrent
users) →
load-test-bootstrap. CrUX/RUM data is about real historical
traffic, not a controlled stress scenario.
Phase 0 — Data source discovery
- Check whether the site has enough traffic for CrUX: query the public
Chrome UX Report API
(
https://chromeuxreport.googleapis.com/v1/records:queryRecord,
free, needs only a Google API key — ask the user if they have one
configured, or use the PageSpeed Insights UI as a manual fallback,
which surfaces the same CrUX data without a key) for the site's origin
and up to 5 key URLs.
- In parallel, check whether the project already has its own RUM/field
data: a GA4 property with the Web Vitals event, a Cloudflare Web
Analytics Core Web Vitals panel, Sentry Performance, Datadog RUM, or
similar — read the project's own docs (AGENTS.md/README/analytics
setup notes) before assuming nothing exists.
- If neither source has usable data (too little traffic for CrUX, no RUM
tool installed) — that absence IS the Phase 3 finding. Stop here
rather than fabricating a comparison.
Phase 1 — Compare
For each URL with field data available, pull the p75 values for LCP, INP,
and CLS (CrUX and most RUM tools report p75, matching Google's own "Good"
threshold methodology) and line them up against the lab Lighthouse values
for the same URL.
| URL |
Metric |
Lab (Lighthouse) |
Field p75 |
Threshold |
Status |
| ... |
LCP |
... |
... |
<2.5s |
✅/⚠️/❌ |
| ... |
INP |
... |
... |
<200ms |
✅/⚠️/❌ |
| ... |
CLS |
... |
... |
<0.1 |
✅/⚠️/❌ |
Phase 2 — Diagnose divergence
If field is meaningfully worse than lab, the usual causes, roughly in
order of likelihood:
- Device/network mix: lab uses one fixed throttling profile; real
users include low-end devices and slow/lossy mobile networks the lab
profile doesn't represent.
- Third-party scripts: lab runs can execute a clean, warm-cache run;
real users hit cold caches and slow-loading third-party origins (ties
into
third-party-script-audit if this looks like the cause).
- CDN edge cache misses / geographic latency: field data blends every
visitor's actual edge location; lab always tests from one location.
- INP specifically: lab tools often approximate or don't fully
capture real interaction responsiveness under real usage patterns
(scrolling while a heavy script runs, rapid taps) — a real INP problem
can be invisible in a lab run entirely.
If field is better than lab, note it and move on — don't manufacture a
fix for a lab-only artifact nobody actually experiences.
Phase 3 — Findings
One row per URL/metric combination that fails its field threshold, with
the suspected cause from Phase 2 and a link back to the raw comparison
table. If no field data exists at all, that gap itself is the sole
finding — say so plainly rather than silently skipping the review.
1---2name: production-cwv-review3description: Compares real-user field performance data (Chrome UX Report / CrUX, GA4 Web Vitals, Cloudflare Web Analytics, or any RUM tool already in place) against lab data (Lighthouse) to find where they diverge — a page can score well in Lighthouse and still be slow for real users. Works on any public website with enough traffic for CrUX (free, no setup required), or any site with its own RUM/analytics tool already wired in. Trigger on "check real Core Web Vitals", "how is this actually performing for users", "field vs lab performance", or as a sanity check after a Lighthouse-only performance pass. NOT a replacement for the Lighthouse/lab baseline (e.g. nfr-gap-audit's Phase 2) — this is a follow-up cross-check against it. NOT for setting up RUM/analytics from scratch — that's implementation work; flag the absence as a finding and hand it off. NOT for synthetic load/stress testing (use load-test-bootstrap).4---56# Production Core Web Vitals Review78Lighthouse measures one page load, once, under fixed lab conditions. Real9users load the same page on a huge range of devices, networks, and cache10states. This skill closes that gap: pull real-user field data and compare11it against the lab numbers to find pages that look fine in a lab report12but are actually slow (or vice versa) for real visitors.1314Works on any public site — CrUX needs no setup, just enough real traffic15for Google to have collected a stable sample for that origin/URL.1617## When not to use this1819- **No lab baseline exists yet** → run the Lighthouse pass first (this20 project's own baseline step, or `npx lighthouse`/`lhci autorun`); this21 skill needs something to compare field data against.22- **Setting up RUM/analytics that doesn't exist yet** → that's23 implementation work, not an audit. Report "no field data available" as24 the finding and stop there.25- **Synthetic load testing** (how does the site behave under N concurrent26 users) → `load-test-bootstrap`. CrUX/RUM data is about real historical27 traffic, not a controlled stress scenario.2829## Phase 0 — Data source discovery30311. Check whether the site has enough traffic for CrUX: query the public32 Chrome UX Report API33 (`https://chromeuxreport.googleapis.com/v1/records:queryRecord`,34 free, needs only a Google API key — ask the user if they have one35 configured, or use the PageSpeed Insights UI as a manual fallback,36 which surfaces the same CrUX data without a key) for the site's origin37 and up to 5 key URLs.382. In parallel, check whether the project already has its own RUM/field39 data: a GA4 property with the Web Vitals event, a Cloudflare Web40 Analytics Core Web Vitals panel, Sentry Performance, Datadog RUM, or41 similar — read the project's own docs (AGENTS.md/README/analytics42 setup notes) before assuming nothing exists.433. If neither source has usable data (too little traffic for CrUX, no RUM44 tool installed) — that absence IS the Phase 3 finding. Stop here45 rather than fabricating a comparison.4647## Phase 1 — Compare4849For each URL with field data available, pull the p75 values for LCP, INP,50and CLS (CrUX and most RUM tools report p75, matching Google's own "Good"51threshold methodology) and line them up against the lab Lighthouse values52for the same URL.5354| URL | Metric | Lab (Lighthouse) | Field p75 | Threshold | Status |55|-----|--------|-------------------|-----------|-----------|--------|56| ... | LCP | ... | ... | <2.5s | ✅/⚠️/❌ |57| ... | INP | ... | ... | <200ms | ✅/⚠️/❌ |58| ... | CLS | ... | ... | <0.1 | ✅/⚠️/❌ |5960## Phase 2 — Diagnose divergence6162If field is meaningfully worse than lab, the usual causes, roughly in63order of likelihood:64- **Device/network mix**: lab uses one fixed throttling profile; real65 users include low-end devices and slow/lossy mobile networks the lab66 profile doesn't represent.67- **Third-party scripts**: lab runs can execute a clean, warm-cache run;68 real users hit cold caches and slow-loading third-party origins (ties69 into `third-party-script-audit` if this looks like the cause).70- **CDN edge cache misses / geographic latency**: field data blends every71 visitor's actual edge location; lab always tests from one location.72- **INP specifically**: lab tools often approximate or don't fully73 capture real interaction responsiveness under real usage patterns74 (scrolling while a heavy script runs, rapid taps) — a real INP problem75 can be invisible in a lab run entirely.7677If field is *better* than lab, note it and move on — don't manufacture a78fix for a lab-only artifact nobody actually experiences.7980## Phase 3 — Findings8182One row per URL/metric combination that fails its field threshold, with83the suspected cause from Phase 2 and a link back to the raw comparison84table. If no field data exists at all, that gap itself is the sole85finding — say so plainly rather than silently skipping the review.