01 — Monitor

Every deploy re-fires a CrUX scan.

The GitHub push webhook on /api/deploys records the deploy, then immediately re-runs fetchAndPersistCwvfor the property’s pages — the same rescan trigger that ships in the deploy handler. INP, LCP, and CLS against the property’s last-known-good baseline, into a fresh CWV row tagged with the deploy SHA. No Friday-morning drift: a regression that lands in the release lands in the same window as the release.

In the product: operators see the deploy SHA as a commit badge on /properties/:id.

02 — Detect

Each scan is classified.

Every CrUX-triggered scan and every crawl run ends in a classified finding — a Finding.kind tag that decides whether the worker auto-drafts a fix or just files the regression.

In the product: operators see the classified findings in the list on /properties/:id, grouped on the latest crawl run.

  • LOOP

    A cycle in the redirect graph.

    The crawler’s cycle-detection emits a Finding.kind of "redirect_loop" the moment it sees a hop repeat — /shop → /products → /shop. The fan-out downstream already keys on that kind.

  • CANONICAL

    A canonical the page is missing.

    Pages without a self-referential <link rel="canonical"> land on the “canonical” finding kind — the complement to the redirect loop, surfaced through the same classification.

03 — Fix

Open redirect loops become a PR.

The deploy handler fan-out calls runRedirectPrForFinding on every open redirect_loopfinding: clone the property’s GitHub repo, write a fix/seo-* branch with the rewrite that breaks the cycle into a single direct → canonical hop, open the PR, and back-write Finding.htmlUrl so the open PR shows up in-product. The existing /api/pr/redirect handler is the manual one-off equivalent, gated by requireAuth() + per-property scoping, with idempotency on the same htmlUrl write-back so a second fan-out won’t double-open the PR.

In the product: operators see the PR link on the corresponding finding card on /properties/:id once a draft lands.

acme/web · #1842
auto-drafted by Crawlwren

fix(redirects): break /shop → /products → /shop cycle on /shop-sale

fix/seo-redirect-loop-shop-sale
opened by @crawlwren-bot·1 file changed · 2 insertions(+)
  • next.config.js
    +2 · −0
1 async redirects() {
2+ { source: "/shop-sale", destination: "/shop", permanent: true },
3 { source: "/shop", destination: "/products", permanent: true },

Wire it in

See how pricing fits a Friday release.

The plan picker maps straight onto the loop above: Solo runs the scan+draft fan-out on one property; Team pulls the cadence up; Agency unlocks cross-property dashboards. No scraped-contract quotes, no payment on file until you wire in your first property.