Catch the deploy that slowed a page or cost it rankings

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

Runs
Daily at 7:00, week summary on Mondays
Posts
Report card

From your apps to your board

How the skill works: your agent reads map changed files to urls from GitHub; production deploys and their files from Vercel; measure speed on the changed urls from PageSpeed Insights; positions and index state from Google Search Console; repository, vercel project, search console property, key urls to test on every deploy from What you tell it, daily at 7:00, week summary on mondays, and gives you report card and the report in the chat.

Reads

  • GitHubMap changed files to URLs
  • VercelProduction deploys and their files
  • PageSpeed InsightsMeasure speed on the changed URLs
  • Google Search ConsolePositions and index state
  • What you tell itRepository, Vercel project, Search Console property, Key URLs to test on every deploy
reads
Your agentClaude Code, Codex, Cursor or GeminiDaily at 7:00, week summary on Mondays
posts

You get

  • Report cardOn your board, updated in place every run
  • The report in the chatEvery run, with or without Briefkin

What you get

  • 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.

The card on the right is a sample. Yours shows your own numbers and updates in place every run.

What lands on your board

acme-webDeploy-caused SEO regressions: deploys, Core Web Vitals and Search Console · Claude Code

/pricing slowed and 3 changed pages slipped after Tuesday's deploy

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.

Tue 14:02Deploy a1b9c3e to production: "New pricing table, 18 template pages" (23 URLs changed)Tue 14:20PSI mobile /pricing: TBT 210 ms to 940 ms (median of 3), LCP 2.1 s to 2.4 sTue 14:20HTML check: the same deploy changed the /pricing title from "Pricing and plans | Acme" to "Plans"Fri 07:00GSC: /pricing 3.8 to 7.2, /templates 5.1 to 8.4, /features/invoicing 6.0 to 9.3Mon 07:00URL Inspection: 12 of 18 new /templates/* pages "Crawled - currently not indexed"Mon 07:00CrUX field INP for /pricing: 170 ms to 240 ms over 6 days (28-day window)

Start with a1b9c3e: restore the /pricing title and move the new pricing-table script off the main thread.

ResolveFailed
An example with made-up numbers. Your agent posts your real ones.

What it does

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.

How it works

  1. 1List production deploys and their files

    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. 2Map changed files to URLs

    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. 3Measure speed on the changed URLs

    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. 4Check positions and index state

    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. 5

    5Put 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. 6

    6Post 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.

  7. Post it to the board

    With BRIEFKIN_TOKEN set, the agent sends the result as one card that updates in place every run. Without it, you get the report in the chat.

Install and run it

Pick your agent, where the data comes from and how to schedule it; the commands below follow.

Agent
Data
Schedule
  1. Install the skill

    One command. It puts the SKILL.md in ~/.claude/skills and in ~/.agents/skills, which Codex, Cursor, Gemini CLI, Copilot CLI and OpenCode read too.

    shellcurl -fsSL https://briefkin.dev/skills/vercel-deploy-seo-regressions/install | sh
  2. Run it

    Ask Claude Code:

    promptRun the vercel-deploy-seo-regressions skill

    Or paste the prompt instead of installing:

    promptCheck yesterday's production deploys of my site for SEO regressions. From Vercel (or GitHub), list each deploy with its time and commit, and from the GitHub compare API the files it changed; map files to URLs (app router paths, content files, shared components to my key URLs). For up to 10 changed URLs, run PageSpeed Insights mobile 3 times, take median LCP, TBT and CLS, and read field INP, LCP and CLS. Compare with my last baseline. From Search Console, get daily position per changed URL (dataState all) for 14 days before the deploy until now, and run URL Inspection on changed and new URLs. Flag LCP +500 ms, TBT doubled, CLS +0.05, drops of 3+ places on pages with 100+ impressions, and crawled-not-indexed pages. Show one UTC timeline and name the commit to look at first.
  3. Put it on a schedule

    Daily at 7:00, week summary on Mondays, from cron on any machine where Claude Code is signed in:

    crontab0 7 * * * claude -p "Run the vercel-deploy-seo-regressions skill"

    Add it with crontab -e. A laptop asleep at that time runs it when it wakes if you use launchd or Task Scheduler instead; GitHub Actions works for the API route.

  4. Send it to your board

    Connect Briefkin once with curl -fsSL https://briefkin.dev/install | sh and every run lands on your board as a report card. How setup works

View the SKILL.mdRaw file
SKILL.md---
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

What your agent needs

  • 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).

Connecting GitHub

  1. GitHub MCP Server, GitHub's official server, hosted at https://api.githubcopilot.com/mcp/. Sign in with OAuth or a personal access token; add /readonly to the URL, or use a toolset URL such as https://api.githubcopilot.com/mcp/x/pull_requests/readonly, to expose only read tools. It also runs locally (source).
  2. The gh CLI. Run gh auth login once, or set GH_TOKEN for scheduled runs. gh pr list --search "review-requested:@me" --json number,title,url,createdAt and gh api repos/{owner}/{repo}/dependabot/alerts cover most reports.
  3. REST and GraphQL with a fine-grained token. Store it in GITHUB_TOKEN, limit it to the repositories in the report, and grant Read on Metadata, Contents, Pull requests and Issues, plus Dependabot alerts if you report on them.

With every permission on a fine-grained token set to Read, the agent can't push, merge, comment or close anything.

Connecting Vercel

  1. Vercel MCP, Vercel's official remote server at https://mcp.vercel.com, signed in with OAuth. It lists projects and deployments, reads build and runtime logs, and searches Vercel's docs.
  2. The vercel CLI. vercel ls <project> lists recent deployments, vercel inspect <deployment-url> --logs prints the build log, and vercel logs <deployment-url> shows runtime logs. Pass --token $VERCEL_TOKEN and --scope <team> in scheduled runs.
  3. REST API with an access token created under Account Settings > Tokens and scoped to the team, stored in VERCEL_TOKEN, with VERCEL_TEAM_ID sent as teamId.

Vercel tokens can be limited to one team and given an expiry but can't be made read-only, so use a short-lived token for reports and keep deploy and env var tasks out of the same skill.

Connecting PageSpeed Insights

  1. MCP server. Google doesn't publish an MCP server for PageSpeed Insights; community servers wrap the same API. The Chrome DevTools project does publish Chrome DevTools MCP, which records performance traces in a local Chrome and can add CrUX field data: claude mcp add --transport stdio chrome-devtools -- npx -y chrome-devtools-mcp@latest (source). Codex and Cursor run the same command.
  2. PageSpeed Insights API with a key. The API answers occasional requests without a key; for scheduled runs, enable the PageSpeed Insights API in a Google Cloud project, create an API key restricted to it, and store it in PAGESPEED_API_KEY. Enable the Chrome UX Report API on the same key if the skill also reads CrUX history.
  3. Lighthouse CLI. npx lighthouse https://example.com --only-categories=performance --output=json --quiet --chrome-flags="--headless" runs the lab test on your own machine or in CI.

All of this reads public performance data. Restrict the API key to the PageSpeed Insights and Chrome UX Report APIs so a leaked key is useless for anything else.

Connecting Google Search Console

  1. MCP server. Google doesn't publish one for Search Console. Community MCP servers exist; check which scopes one asks for before you sign in.
  2. API with a service account. Enable the Google Search Console API in a Google Cloud project, create a service account, and add its email under Settings > Users and permissions on the property. Point GOOGLE_APPLICATION_CREDENTIALS at the key and set GSC_SITE_URL to sc-domain:example.com or https://www.example.com/.
  3. OAuth as yourself. When you can't add users to the property, a desktop OAuth client with the webmasters.readonly scope and a stored refresh token works for scheduled runs.

Ask for webmasters.readonly and add the service account as a Restricted user; the reports only read.

Questions

Why can a ranking drop show up days after the deploy?

Google has to recrawl the page before a change can move it, and Search Console's final data lags about two days. The skill asks for dataState: "all", which includes fresh but incomplete days, and keeps watching changed URLs for 14 days. Its dates are Pacific time, so they are shifted to UTC before lining up with deploy times.

Why does it show lab TBT and not INP right after a deploy?

INP only exists in field data, and the Chrome UX Report averages the last 28 days, so a slower deploy moves it gradually. Total Blocking Time in the lab run is the closest early signal; the card shows both, and the field number confirms the lab one over the next weeks.

Do I need Briefkin to use the Deploy-caused SEO regressions: deploys, Core Web Vitals and Search Console skill?

No. The skill runs with your own agent and gives you the report in the chat. With a Briefkin token set, each run also posts it to your board as a report card, so you can glance at it instead of asking again.

Which agents can run it?

Claude Code, Codex, Cursor, Gemini CLI, GitHub Copilot CLI, OpenCode, Windsurf and Amp all read SKILL.md files. The install command puts it in ~/.claude/skills and ~/.agents/skills, where they look. ChatGPT, the Claude app and Grok take the prompt instead. Pick your agent under "Install and run it" and the commands change to match.

Can it work without an API or MCP server, from my browser?

Yes. Switch the data source to "Your browser" and the steps change to the GitHub, Vercel, PageSpeed Insights and Google Search Console pages themselves: the agent opens vercel.com/dashboard in a browser you are signed in to and reads the numbers off the page. Claude Code, Codex, Cursor, Claude app and ChatGPT can drive a browser out of the box; Gemini CLI, Copilot CLI and OpenCode need a Playwright or Chrome DevTools MCP server first. It is slower than the API and needs you signed in, so it suits a report you run by hand or from a scheduler on your own computer.

Read this report on your board, not in a chat log

Skills are free to use with any agent. Briefkin is where their results land: one board for every report, number and question your agents send, with a ping when something needs you.

Free during the open beta. No card.