Last reviewed: 2026-08-26

Direct answer

Make web performance a merge contract that the coding agent cannot quietly rewrite. Define a route inventory, use the same production-like build and browser settings for every pull request, collect several Lighthouse runs, and make the important assertions fail when a budget is exceeded. Upload the report as a workflow artifact, then make one uniquely named status check required on the protected branch. The agent may suggest a fix, but it cannot make a regression disappear by changing the target in the same opaque step.

Chrome for Developers’ Lighthouse overview describes Lighthouse as an open-source automated tool for auditing pages in DevTools, from the command line, or as a Node module. It also identifies Lighthouse CI as a way to prevent regressions. The Lighthouse CI configuration reference documents repeated collection, the assert command, numeric thresholds, and warn versus error severity. An error assertion produces a failing exit status; a warn assertion reports a problem without failing the command.

Start with a versioned contract, not a magic number in an agent prompt. For example, a checkout route might require a performance score of at least 0.90, first contentful paint below 2,500 milliseconds, and largest contentful paint below 4,000 milliseconds under one declared CI profile. These are illustrative targets, not universal promises. Establish your values from a stable build, review them like code, and change them only with before-and-after evidence.

Who this is for

This guide is for frontend engineers who let coding agents edit templates, components, CSS, JavaScript, or bundler settings; platform engineers who own pull-request workflows; and engineering leads who need a defensible reason to merge or reject an automated patch. It is most useful for repositories with fast UI iteration, several routes with different payloads, or agents that can repeatedly revise a patch after CI feedback.

It is not a substitute for real-user measurement, accessibility review, or a human assessment of product behavior. It is a repeatable pre-merge control. Pair it with the change evidence packet workflow when a reviewer needs the agent’s scope, touched routes, and threshold rationale in one place.

Key takeaways

  • Audit the optimized artifact that the release would serve, not an unbuilt development server.
  • Keep a route-and-metric table with the route, metric, threshold, severity, build identifier, browser strategy, and budget version.
  • Use several runs and a fixed collection count. A single unusually fast result must not reset a contract.
  • Put blocking error assertions on user-visible risks; keep exploratory checks at warn until their variance is understood.
  • Review application and budget changes together, but require an explicit explanation for every threshold change.
  • Give the workflow job a unique name. GitHub’s protected-branch guidance warns that duplicate names can make required checks ambiguous.
  • Upload the raw report and a compact summary. GitHub’s artifact guide documents uploading build and test output and setting artifact-specific retention.
  • Log measurements and identifiers, not cookies, page text, form values, authorization headers, or raw query strings.

For a focused handoff after the gate runs, use the reviewable-diff checklist .

Sources checked

These public sources inform this article:

  1. Introduction to Lighthouse | Chrome for Developers — audit modes, generated reports, command-line and Node workflows, and Lighthouse CI’s regression-prevention role.
  2. Lighthouse CI configuration — collection runs, assert, threshold properties, severity levels, and failure status.
  3. About protected branches | GitHub Docs — required checks, accepted check states, and unique job names.
  4. Store and share data with workflow artifacts | GitHub Docs — report upload, download, job handoff, and retention settings.

Contract details to verify

Create the contract beside the workflow. Each row should identify a deterministic route, the build mode, browser strategy, metric, threshold, severity, and owner. If a route needs fixture data, seed a known fixture and record its version; do not let an agent invent content during the audit. Keep the canonical thresholds in one checked-in Lighthouse CI file so a prompt cannot override them unnoticed.

This example uses a local preview server and harmless values. Replace the route list and thresholds with reviewed values for your application.

ci:
  collect:
    startServerCommand: "npm run preview -- --host 127.0.0.1 --port 4173"
    startServerReadyPattern: "ready|listening"
    url:
      - "LOCAL_PREVIEW_ORIGIN/"
      - "LOCAL_PREVIEW_ORIGIN/pricing"
    numberOfRuns: 3
  assert:
    assertions:
      "categories:performance": ["error", {"minScore": 0.90}]
      "first-contentful-paint": ["error", {"maxNumericValue": 2500}]
      "largest-contentful-paint": ["error", {"maxNumericValue": 4000}]
      "total-blocking-time": ["warn", {"maxNumericValue": 300}]
  upload:
    target: "filesystem"
    outputDir: "./artifacts/lighthouse"

Verify option spelling against the installed Lighthouse CI version. The reference supports configuration files and command-line overrides, but the file should remain the source of truth. Run the audit in a uniquely named job such as agent-web-performance-gate. If build, audit, and upload are separate jobs, make the audit result the required check and make later jobs depend on it.

A minimal command sequence can expose the decision without swallowing failures:

set -eu
npm ci
npm run build
npx lhci autorun --collect.numberOfRuns=3

Keep artifact upload visible even when assertions fail. A report-upload problem and a performance failure are different events; record both rather than turning either into a misleading green result.

Define sanitized JSON-lines fields before implementation. Useful fields include run_id, commit_sha, route, device_strategy, browser_version, build_id, budget_version, metric, threshold, observed, severity, decision, duration_ms, artifact_path, and started_at. Omit page content, cookies, authorization headers, session identifiers, form values, and full request URLs. If a route has a query string, log a stable name such as search-default. Keep a compact summary separate from a full report if the report may contain page text.

{
  "run_id": "example-run",
  "commit_sha": "example-commit",
  "route": "pricing",
  "metric": "largest-contentful-paint",
  "threshold": 4000,
  "observed": 3520,
  "severity": "error",
  "decision": "pass",
  "artifact_path": "artifacts/lighthouse"
}

The happy path is straightforward:

  1. The agent opens a pull request and lists the routes and UI assets it touched. A human confirms the route inventory covers the change.
  2. CI builds the production-like artifact from the pull-request commit and starts the known preview command.
  3. Lighthouse CI collects each configured route three times, writes the report directory, and evaluates every assertion.
  4. The job emits the sanitized summary, uploads the report artifact, and returns success only when every error-level assertion passes.
  5. Branch protection allows the merge after the unique required check and ordinary review requirements are satisfied.

The error path must be just as explicit:

  1. Stop the merge and preserve the failing report, commit identifier, route, metric, threshold, and observed value.
  2. Re-run the same commit under the same profile. If the failure is not reproducible, investigate runner or server stability before changing a budget.
  3. If it is reproducible, ask the agent to locate the changed asset, request, or rendering path and propose a narrow fix. Do not ask it to raise the threshold first.
  4. Re-run the unchanged contract after the fix. If the target truly needs to move, submit that threshold change separately with reviewer-owned rationale and before-and-after evidence.

Finally, configure the protected branch to require the performance job. GitHub documents that required checks must be successful, skipped, or neutral and that all required checks must pass before a protected branch accepts a merge. Verify the rule with a test pull request rather than assuming a similarly named job is the same check.

Failure modes

The agent edits the budget and the feature together. A green result then hides the trade-off. Require a reviewer to inspect threshold diffs and consider separate ownership for the budget file.

A score hides a metric regression. A category score can stay acceptable while an individual paint or blocking-time value worsens. Keep numeric assertions for specific user-visible risks; use the score as a summary.

One noisy run blocks good work. CPU contention, cold caches, or a delayed preview server can move measurements. Multiple runs expose variance, but they do not make an unstable environment trustworthy. Record the environment and fix repeatability before widening limits.

The wrong build is audited. The agent may change source files while the workflow serves an old directory, a development build, or a fallback shell page. Log build_id, verify the generated directory, and confirm that every URL returns the intended page.

Warnings are mistaken for gates. A warning reports a result without a non-zero failure; an error-level assertion is the blocking signal. Review the severity column and the final process exit status.

The required check is ambiguous. Duplicate job names across workflows can make a required check ambiguous. Give the performance job one stable name and avoid reusing it for unrelated matrices.

The report disappears. Upload the report directory as an artifact even on failure, and set a retention period that covers the review window. Artifacts support debugging; the status check controls the merge.

The route list drifts. A green gate that never visits a newly changed route is false assurance. Require a route-impact note and periodically compare the inventory with the application’s navigation and deployment manifest.

The agent retries until the sample looks good. Repeated attempts can create selection bias. Pin the commit, retain every run summary, and use a predeclared collection count. A rerun should investigate instability, not search for a favorable number.

FAQ

Does Lighthouse CI replace human review?

No. Lighthouse describes failed audits as indicators for improvement. The gate answers a narrow question about repeatable measurements; a reviewer still decides whether the change is correct, intentional, and appropriate for the product.

Which assertion level should block a merge?

Use error for a contract you are prepared to enforce. Use warn while learning variance or migrating a route. Promote a warning only after the team agrees on the measurement and its owner.

How many runs should we use?

Choose a small, repeatable number and record it in the contract. Lighthouse CI supports numberOfRuns; three is a reasonable starting point for a lightweight pull-request gate, but runner stability and execution time determine the right value. More runs cannot compensate for serving the wrong build.

Can an authenticated page be audited?

The Lighthouse overview says pages can be public or require authentication and supports command-line and Node workflows. Use a controlled, non-production fixture and keep access material out of logs and artifacts. If the route is not deterministic, exclude it explicitly and document the separate test that covers it.

What if the agent asks to raise a threshold?

Treat that as a contract change. Request before-and-after measurements, the affected route, the reason the old target is no longer realistic, and a reviewer who owns the budget. Do not bundle the threshold change silently with unrelated UI edits.

How long should reports be retained?

Retain them for the normal pull-request review and incident window, subject to repository policy. GitHub’s artifact workflow supports a per-artifact retention setting; preserve useful evidence without storing unnecessary page content.

What if the route needs a sign-in?

Use a dedicated fixture and keep its access material outside the repository and outside logs. Review the report for accidental page data before upload. If the fixture is unavailable, fail closed with an explicit “not measured” result rather than treating a missing route as a pass.

Reader next step

Pick one high-traffic route that coding agents frequently change. Write down three measurable budgets, run the same production-like build three times, and commit the contract beside the workflow. Add a uniquely named agent-web-performance-gate job, upload its report directory, and require the check on the protected branch. Open a small agent-generated pull request, exercise both passing and failing paths, and have a human verify that the sanitized log and retained artifact explain the decision. Once that loop is repeatable, expand the route inventory one page at a time.