---
name: vercel-deploy-seo-regressions
description: "After each production deploy: the routes it changed, their lab speed, field Core Web Vitals, positions and index state, on one timeline, every morning. Use when the user asks for this, or when it runs on a schedule."
---

# Deploy-caused SEO regressions: deploys, Core Web Vitals and Search Console

After each production deploy: the routes it changed, their lab speed, field Core Web Vitals, positions and index state, on one timeline, every morning.

A deploy that renames a title tag, ships a heavy script or adds a stray `noindex` looks fine in the build log. The damage turns up days later in Search Console, by which time three more deploys have gone out and nobody can say which one did it.

Every morning the agent takes yesterday's production deploys, works out which URLs each one changed, runs PageSpeed Insights on them, inspects their index state and watches their positions over the following days. Anything that got worse after a deploy, on a page that deploy touched, lands on one timeline with the commit next to it.

## Two ways to get the data

Use the API or MCP route when you have an MCP server, a CLI or an API token for the app. Otherwise, if you can drive a browser the user is signed in to (Claude in Chrome, a Playwright or Chrome DevTools MCP server), use the browser route. Say which one you used.

## Route A: API or MCP server

### What you need

- A list of key URLs to test on every deploy (home, pricing, sign-up, top landing pages), and a sitemap the agent can read to expand dynamic routes.
- Read access to the GitHub repo (`gh` or a fine-grained token with Contents read), a Vercel token for the team or the Vercel MCP server, and a PageSpeed Insights API key in `PAGESPEED_API_KEY`.
- Search Console access on the property, with the URL Inspection API (about 2,000 inspections per property per day).

If something is missing, say what and stop; do not invent numbers.

### Steps

1. **List production deploys and their files** (Vercel): Call `GET /v7/deployments?projectId=<id>&target=production&state=READY&since=<ms 24h ago>` and keep `uid`, `createdAt` and `meta.githubCommitSha`. With GitHub, compare each commit with the previous production one: `GET /repos/{owner}/{repo}/compare/{prev}...{sha}` and read `files[].filename` and `status`.
2. **Map changed files to URLs** (GitHub): Turn files into routes: `app/(marketing)/pricing/page.tsx` is `/pricing` (route groups drop out), a `[slug]` segment expands to its URLs from the sitemap, and `content/blog/<slug>.mdx` is `/blog/<slug>`. A change to a shared layout or component maps to every URL in your key-URL list. For each changed URL, fetch the live HTML and compare `<title>`, `meta name="robots"` and `link rel="canonical"` with the snapshot from the last run.
3. **Measure speed on the changed URLs** (PageSpeed Insights): For up to 10 changed URLs, call `runPagespeed?url=<url>&strategy=mobile&category=performance` three times and take the median `numericValue` of the `largest-contentful-paint`, `total-blocking-time` and `cumulative-layout-shift` audits. Read field data from `loadingExperience.metrics` (`INTERACTION_TO_NEXT_PAINT`, `LARGEST_CONTENTFUL_PAINT_MS`, `CUMULATIVE_LAYOUT_SHIFT_SCORE`: `percentile` and `category`). Flag LCP up 500 ms or more, TBT doubled and up 200 ms or more, CLS up 0.05 or more, or a field metric turning `SLOW`.
4. **Check positions and index state** (Google Search Console): Call `searchanalytics.query` with `dimensions: ["page", "date"]` and `dataState: "all"` for the changed URLs, from 14 days before each deploy to today; flag a drop of 3 places or more on a page with 100 or more impressions. Call `urlInspection.index.inspect` on each changed or new URL and read `indexStatusResult.coverageState`, `lastCrawlTime`, `googleCanonical` and `userCanonical`; flag `Crawled - currently not indexed` with a crawl after the deploy, and a Google canonical that differs from yours.
5. **Put it on one clock**: Convert everything to UTC (Search Console dates are Pacific time). A finding counts only when it starts after the deploy and the URL is in that deploy's changed list; anything else is listed as unrelated. Keep the lab medians and HTML snapshots in `seo-deploy-baseline.json` between runs, and read the previous card with `GET https://briefkin.dev/v1/cards/insight-vercel-deploy-seo-regressions` so an open regression is updated rather than reported again.
6. **Post the card**: Post an update card with a timeline of deploy, lab run, position change and index result, and a table of affected URLs. Set `work_state: "failed"` when any regression is tied to a deploy. On Mondays, post the week's deploys and their outcomes instead.

## Route B: the browser

### What you need

- Signed in to Vercel and GitHub with access to the project, and to Search Console with access to the property.

If something is missing, say what and stop; do not invent numbers.

### Steps

Start at https://vercel.com/dashboard. The user is signed in already; never type passwords or codes, and ask if a sign-in page appears.

1. **Find yesterday's production deploys**: Open the project, then Deployments, and filter the environment to Production. For each deploy from the last 24 hours, note the time, the commit and its message, and open the commit on GitHub to read the list of changed files.
2. **Run PageSpeed Insights**: Open `https://pagespeed.web.dev/`, enter each changed key URL (up to 10) and run it three times on Mobile. Note the median Largest Contentful Paint, Total Blocking Time and Cumulative Layout Shift, and the field values in "Discover what your real users are experiencing" when shown.
3. **Inspect the changed URLs**: In Search Console, paste each changed or new URL into the inspection bar at the top. Read the page indexing status, last crawl date and the Google-selected canonical.
4. **Compare positions**: Open Performance > Search results, add a Page filter for each changed URL, and compare the 14 days before the deploy with the days since (Date > Compare > Custom). Flag drops of 3 places or more on pages with 100 or more impressions.
5. **Write the timeline**: Put the deploy, the speed result, position changes and index results in time order, in UTC. Only count a problem on a URL the deploy changed, starting after it.

Note: The browser route has no baseline from the last run unless you keep notes, so it compares against the PageSpeed numbers you read before the deploy, if any. Three PageSpeed runs per URL take a minute each. URL Inspection in the UI is one URL at a time; past 20 URLs the API route is the better one.

## The report

- **Timeline**: each production deploy with its commit, then the lab run, position changes and index results for the URLs it changed.
- **Speed regressions**: URL, metric, before and after, and the field metric's category.
- **Ranking drops** on changed URLs, with any title, robots or canonical change in the same deploy.
- **Index problems**: changed or new URLs crawled but not indexed, or with a different Google canonical.
- The commit to look at first.

## Post it to Briefkin

When `BRIEFKIN_TOKEN` is set, or `~/.config/briefkin/env` exists (the installer from https://briefkin.dev/docs writes it), also send the result to the user's Briefkin board as one card. Keep the `item_key` the same on every run so the card updates in place, use a new `event_id` per run, and put today's real numbers in place of the example ones. If the request fails, say so once and carry on.

```bash
[ -n "${BRIEFKIN_TOKEN:-}" ] || . ~/.config/briefkin/env
curl -sS -X POST "${BRIEFKIN_URL:-https://briefkin.dev}/v1/events" \
  -H "authorization: Bearer $BRIEFKIN_TOKEN" \
  -H "content-type: application/json" \
  -d @- <<'JSON'
{
  "v": 1,
  "event_id": "vercel-deploy-seo-regressions/<date of this run, e.g. 2026-10-16>",
  "item_key": "vercel-deploy-seo-regressions",
  "project": "acme-web",
  "source": "Deploy-caused SEO regressions: deploys, Core Web Vitals and Search Console skill",
  "kind": "update",
  "title": "/pricing slowed and 3 changed pages slipped after Tuesday's deploy",
  "summary": "Deploy `a1b9c3e` (Tue 14:02 UTC) changed 23 URLs. Lab Total Blocking Time on /pricing went from **210 ms to 940 ms**; field INP has moved from 170 ms to 240 ms, out of the good range. **3 changed URLs** lost 3 or more places, and **12 new template pages** are crawled but not indexed.",
  "work_state": "failed",
  "widget": [
    {
      "type": "Log",
      "props": {
        "lines": [
          {
            "time": "Tue 14:02",
            "text": "Deploy a1b9c3e to production: \"New pricing table, 18 template pages\" (23 URLs changed)",
            "level": "info"
          },
          {
            "time": "Tue 14:20",
            "text": "PSI mobile /pricing: TBT 210 ms to 940 ms (median of 3), LCP 2.1 s to 2.4 s",
            "level": "error"
          },
          {
            "time": "Tue 14:20",
            "text": "HTML check: the same deploy changed the /pricing title from \"Pricing and plans | Acme\" to \"Plans\"",
            "level": "warn"
          },
          {
            "time": "Fri 07:00",
            "text": "GSC: /pricing 3.8 to 7.2, /templates 5.1 to 8.4, /features/invoicing 6.0 to 9.3",
            "level": "error"
          },
          {
            "time": "Mon 07:00",
            "text": "URL Inspection: 12 of 18 new /templates/* pages \"Crawled - currently not indexed\"",
            "level": "warn"
          },
          {
            "time": "Mon 07:00",
            "text": "CrUX field INP for /pricing: 170 ms to 240 ms over 6 days (28-day window)",
            "level": "warn"
          }
        ]
      }
    },
    {
      "type": "Callout",
      "props": {
        "text": "Start with a1b9c3e: restore the /pricing title and move the new pricing-table script off the main thread.",
        "tone": "bad"
      }
    }
  ]
}
JSON
```

Widgets come from the Briefkin catalog (https://briefkin.dev/v1/catalog). Without a token, just give the user the report.

## Run it on a schedule

Suggested: Daily at 7:00, week summary on Mondays (`0 7 * * *`). From cron, with the agent signed in on that machine:

```bash
0 7 * * * claude -p "Run the vercel-deploy-seo-regressions skill"
# Codex: codex exec, Gemini CLI: gemini -p, Copilot CLI: copilot -p, OpenCode: opencode run
```

Or use the agent's own scheduler (Claude Code routines, Codex automations, Claude or ChatGPT scheduled tasks): https://briefkin.dev/skills/vercel-deploy-seo-regressions#schedule

Source: https://briefkin.dev/skills/vercel-deploy-seo-regressions
