Lab data will tell you it is fine
Lighthouse does not score INP at all — it scores a load-time proxy — so a 98 performance score is a statement about a page nobody interacted with, and the only instrument that can contradict it is field data from real users.
You have a number now: an INP for the stop button, taken in your own browser, under throttling, written into MEASUREMENTS.md. Suppose it was 480ms. Poor, by the threshold.
Now run Lighthouse against the same page. Performance: 96. Green ring, no responsiveness warnings, LCP and CLS both fine. Two instruments, same page, contradictory verdicts — and the one that looks more official, that produces a shareable artifact with a number in a circle, is the one that is wrong about your users.
This is not a bug and not a calibration problem. Lighthouse’s default performance score does not contain INP at all. It cannot disagree with your measurement, because it never took one.
What the Lighthouse performance score is actually made of
Chrome’s own performance scoring documentation gives the composition for Lighthouse 10:
- Total Blocking Time — 30%
- Largest Contentful Paint — 25%
- Cumulative Layout Shift — 25%
- First Contentful Paint — 10%
- Speed Index — 10%
Five metrics. INP is not among them, and does not appear anywhere on that page. Four of the five are load-time metrics; the fifth, Total Blocking Time, is the responsiveness stand-in, and the same page describes it as measuring “how much time a page is blocked from responding to user inputs.”
Read that definition against a stream. TBT is computed over the page load window — it is a load metric wearing responsiveness vocabulary. Your stream begins after that window has closed, in response to a prompt the user typed, on a page Lighthouse has already finished scoring. Thirty percent of your score is a proxy for a property of a period of time in which your streaming code has not run.
Where people get burned
Check the date stamp at the bottom of that scoring page: it reads Last updated 2019-09-19 UTC, on a page whose content describes Lighthouse 10. Score weightings have changed several times across Lighthouse versions, and the page says so itself. Verify the composition against whatever version your CI is actually pinned to before you build an argument on these percentages. A stale date stamp on a vendor’s own scoring documentation is exactly the kind of thing to notice out loud rather than quietly assume away.
Lighthouse is not incapable — it is being asked the wrong question
Be precise about the accusation, because the imprecise version is easy to refute. Lighthouse can measure INP. Chrome’s INP tool support post (17 May 2022) states that the timespan mode report “now includes INP and an audit to help diagnose any responsiveness issues.” Timespan mode records while you interact, rather than analysing a cold page load, and the user-flows article describes navigation mode as the standard behaviour of analysing “the cold load of a page.”
So the real statement is narrower and more useful: the thing everyone runs — navigation mode, the big score, the number in CI — does not include INP, and the mode that does include it is one nobody runs by default because it requires someone to sit there and interact. Your 96 is not a lie. It is a correct answer to “how did this page load,” being read as an answer to “how does this page respond.”
Why a lab test cannot fix this by trying harder
The obvious response is to automate the interaction — script a click on the stop button, run it in CI, get a lab INP. Worth doing, and it still will not substitute for field data. web.dev’s lab-versus-field article puts the limit plainly: “Lab tests, even those that support script user behavior, cannot accurately predict when users will choose to interact with a page.”
When is the whole game for a streaming surface. A scripted click at token 5, while the transcript is short and the main thread is merely busy, produces a completely different number from a real user’s click at token 900, when the DOM is large, forty markdown blocks have been re-parsed, and memory pressure has started to bite. You get to choose the moment in the lab. Your users choose it in the field, and they choose it precisely when they are most annoyed, which correlates with the stream having been going on the longest.
The same article names two specific holes in TBT as an INP proxy: it does not consider tap delay, and it does not consider user behaviour — so a page can have poor INP while showing good TBT. That is not a hypothetical for you. That is the exact shape of your failure.
What field data is, and what it costs to get
Field data means measurements from real users on real devices. There are two ways to get it, and they are not interchangeable.
CrUX: free, public, and not yours to shape
The Chrome User Experience Report is, in Google’s description, a dataset reflecting “how real-world Chrome users experience popular destinations on the web.” The lab-versus-field article describes it as a distribution of performance metrics from real Chrome users over a 28-day period, with field scores taken at the 75th percentile — the same percentile the thresholds are defined at.
Two eligibility filters decide whether you get any data at all, and both matter for the kind of work you are heading into. CrUX’s methodology page lists the user-side criteria: the user must have usage statistic reporting enabled, be syncing their browser history, have no sync passphrase set, and be on desktop Chrome (Windows, macOS, ChromeOS or Linux) or Android Chrome. No Safari. No Firefox. No signed-out users. No one with a sync passphrase.
And on the site side, the CrUX overview states that origins and pages must be “publicly discoverable and there must be a large enough number of visitors in order to create a statistically significant dataset.” That sentence removes most of the surfaces you are about to build. An agentic panel inside an authenticated B2B application is not publicly discoverable. It will never appear in CrUX, at any traffic volume.
RUM: yours, and the only option that works behind a login
Real user monitoring means you collect it yourself — the onINP call from the previous lesson, reporting to an endpoint instead of the console. It works on authenticated pages, it works in every browser that implements the underlying Event Timing API, it can carry your own dimensions (which host product, which model, how long the stream ran), and it is the only field instrument available for most of what you are going to ship.
The costs are real and worth stating rather than glossing. You are adding a beacon to somebody else’s page, which is a privacy and payload conversation with the host team before it is a technical one. Browser coverage is not uniform — the Event Timing API only reached Baseline in December 2025, so your dataset skews toward recently updated browsers. And you need enough sessions for a 75th percentile to mean anything; a p75 computed over eleven sessions is a decoration.
Check your recall
Answer from memory — no scrolling back.
Retrieval check
Given all of the above, is there any reason left to run Lighthouse on a streaming agent surface?
Check your answer
Yes, and it is worth being clear about so the lesson does not read as “Lighthouse is useless.” It is the cheapest available regression guard on everything except interaction latency: payload size, render-blocking resources, image handling, LCP on the initial shell, layout stability before the user engages. For a component that gets grafted into a host page, the load-time cost you add is a genuine part of your budget and Lighthouse measures it directly.
What changes is the claim you allow it to support. “Lighthouse is green, so the widget does not slow their page down on load” is defensible. “Lighthouse is green, so the widget is responsive” is not a weaker version of the same claim — it is a different claim, about a metric the tool did not collect.
Hands on
Put the two instruments side by side and make them contradict each other
Done when: MEASUREMENTS.md contains a Lighthouse navigation-mode performance score for the chatbot alongside your measured INP for the stop button, plus a written line naming which field-data source is actually available to this surface and why the other one is not.
- Run Lighthouse in navigation mode against the chatbot — DevTools Lighthouse panel, mobile preset, default settings. Record the performance score and the five sub-metric values in
MEASUREMENTS.mdunder a Lab heading. - Directly underneath it, restate the INP you recorded in the previous lesson. Do not average them, reconcile them, or explain the gap yet — the point is to have both numbers on one screen, disagreeing.
- Now run Lighthouse in timespan mode: start the timespan, send a prompt, click stop mid-stream, end the timespan. Note whether its INP is closer to your own measurement than the navigation score suggested, and write down which mode you would put in CI and which you would not.
- Answer in writing, for the flight chatbot specifically: is it publicly discoverable and popular enough to appear in CrUX? Check by putting its URL into PageSpeed Insights and seeing whether any field section appears at all. Record the answer, including a “no data” answer — that is a finding, not a failed step.
- Write two sentences naming the field-data source this surface would actually have to use in production, and the one blocker to standing it up (a beacon endpoint, a host team’s privacy review, session volume — whichever is true).
- Bring the pair of numbers into the chat. I will ask you which one you would show a host team’s engineering lead, and why the other one is the one they would show you.
What this does not cover
Knowing that INP is the number and that it has to come from the field tells you nothing about which part of your code is spending it. The profiling lesson does that: recording a trace while the stream is running, reading the interactions track to find the phase, then the flame chart to find the function, with CPU throttling set so the failure is reproducible on hardware far too fast to feel it unaided.
Standing up a real RUM pipeline — a beacon endpoint, sampling, dimensions, alerting on a p75 regression — is deliberately out of scope here. This course stops at the number and the method that produces it. What lands in the budget reference is a threshold and how it is measured, not the telemetry plumbing that would carry it in production.
Read this next — primary source
Why lab and field data can be different (and what to do about it)web.dev — free; last updated 18 July 2022, which is old enough that INP is barely in it. Google explaining the limits of Google’s own tooling.
This lesson takes the headline finding — that a lab test cannot predict when a user will interact — and the specific gaps in Total Blocking Time as a stand-in. The full article goes considerably further into the mismatches this lesson skips: how a lab test’s single fixed device and connection differ from a distribution of real ones, why cached versus uncached loads diverge, how single-page-app navigation is invisible to a load-based test, and how bot and A/B traffic distort the comparison. Note the 2022 date as you read it: it predates most of the INP tooling, so treat its INP-specific advice as a floor, not a current picture.
Stuck, curious, or think this lesson is wrong? Ask your teaching agent. The lessons are the scaffold; the conversation is where the learning gets unstuck.