Baseline Inheritance for New Routes
A route that shipped yesterday has no history. It has no field sessions in your Real User Monitoring store, no run of green builds behind it, and no per-URL baseline to gate against. Yet the moment it goes live it is subject to the same performance budget as everything else, and your CI job has to decide whether a pull request that touches it should pass or fail. This page is part of the Segmenting Baselines by Page Type reference, and it explains how to bridge that gap: give the new route a provisional inherited baseline drawn from its page-type family, widen the tolerance while it is unproven, then promote it to its own baseline once it has accumulated enough real data. The mechanics of computing a stable per-URL baseline are covered in Historical Baseline Calibration; here we focus on what to gate against before that history exists.
The goal is not to exempt new routes from the budget. An exemption is an open door: teams ship a slow route, forget the temporary waiver, and the regression becomes permanent. Inheritance keeps the route gated from day one against a defensible number, then tightens automatically as evidence arrives.
The Cold-Start Problem for New Routes
When a URL first appears, three inputs your gate normally relies on are missing at once. There is no rolling median of past Largest Contentful Paint values, so a regression check has nothing to compare against. There is no field distribution, so you cannot read a P75 from your own users on that path. And there is no green-build lineage, so automated baseline promotion has no prior commit to anchor to. Faced with this, teams tend to pick one of three bad defaults.
The first bad default is to apply the site-wide global budget verbatim. A checkout confirmation page and a media-heavy article are held to the same LCP ceiling, which is either too loose for the light page or impossibly tight for the heavy one. The second is to skip the gate entirely for any URL the baseline store has never seen, which is exactly when a route is most likely to ship a regression unnoticed. The third is to hand-write a one-off threshold in a config file, which nobody remembers to revisit and which drifts out of step with the family it belongs to.
Inheritance replaces all three with a rule: a route with no baseline of its own borrows the baseline of the page-type family it belongs to, marked provisional, until it earns its own. The classification that decides which family a route joins is the same one described in Per-Template Baselines for CMS Sites — usually the template or layout that renders the route.
Inheriting From the Template Family
The inherited value is not a copy of any single sibling. It is the family baseline — the aggregate that summarizes every established route rendered by the same template. Computing it is the same aggregation you already run for a segment: take the current per-URL baselines of the mature routes in the family and take their median, or a traffic-weighted percentile if some routes dominate volume. For a content-article family measured at P75 on a mid-range Android phone over an emulated Fast 3G connection, that family baseline might land at an LCP of 3100 ms; for a logged-in dashboard family measured the same way it might be 4200 ms, which is why the split described in Separate Baselines for Logged-In and Anonymous Users matters before any route inherits.
Two details keep the inherited number honest. First, inherit at the same percentile and under the same device-and-connection context you gate everything else at — mixing a lab P90 desktop number into a field P75 mobile budget produces a threshold nobody can reason about. Second, inherit the metric set as a bundle. A new route should borrow the family's LCP, its Interaction to Next Paint, and its Cumulative Layout Shift together, so the provisional budget is internally consistent rather than stitched from three unrelated families.
A Provisional Budget With Widened Tolerance
An inherited baseline is a good estimate of where the new route should land, but it is only an estimate — the new route may legitimately differ from its siblings because of a heavier hero image or an extra above-the-fold widget. If you gate the new route at the family baseline with your normal regression tolerance, you will generate false failures for routes that are fine but not identical to the median. The fix is to widen the tolerance while the baseline is provisional and narrow it as confidence grows.
Concretely, if your established routes fail when a metric exceeds baseline by more than 10 percent, a provisional route should be allowed a wider band — commonly 20 to 25 percent — over the inherited value. With a family LCP baseline of 3100 ms at P75 on a mid-range mobile device over Fast 3G, a 20 percent provisional band gates the new route at 3720 ms rather than 3410 ms. That extra room absorbs legitimate per-route variation without opening the door to a genuine regression: a route that comes in at 5200 ms still fails loudly. The widened band is a temporary allowance tied to the provisional flag, not a permanent second budget.
Promoting to an Own Baseline After N Field Sessions
Inheritance is a scaffold, not a home. The moment a route has enough of its own data to compute a stable baseline, it should stop borrowing and start gating on itself with the normal narrow tolerance. The trigger is a count threshold — promote once the route has accumulated at least N field sessions at the gating percentile, where N is large enough that the P75 you read is not dominated by a handful of outlier sessions. For most teams N sits between 500 and 1000 P75-qualifying sessions collected over a rolling window; the reasoning behind picking a session count for percentile stability is developed in Interpolating Percentiles From Sparse Samples.
Promotion should be automatic and observable. When the session count crosses N, compute the route's own baseline from its field distribution, write it to the baseline store, clear the provisional flag, and restore the standard tolerance. From the next build onward the route is gated on itself. If your baseline store lives in a Lighthouse CI server or a database that also records green-main history, wire promotion into the same job that runs after green main builds so a single pipeline owns the transition.
The relationship between the widened provisional band and the session count is worth tabulating, because the two should ratchet together — as sessions accumulate you can narrow the band even before full promotion.
| Sessions collected (P75-qualifying) | Baseline source | Tolerance band | Device + connection |
|---|---|---|---|
| 0 | Family median (inherited) | +20% | Mid-range mobile / Fast 3G |
| 1 to 199 | Family median (inherited) | +20% | Mid-range mobile / Fast 3G |
| 200 to 499 | Family median (inherited) | +15% | Mid-range mobile / Fast 3G |
| 500 to 999 | Blend: 50% family, 50% own | +12% | Mid-range mobile / Fast 3G |
| 1000 or more | Own baseline (promoted) | +10% | Mid-range mobile / Fast 3G |
Configuration
The rules above are only useful if they live in checked-in configuration rather than a person's memory. The snippet below defines the inheritance policy declaratively: it names the family, the metrics to inherit, the provisional band, and the promotion threshold. Store it next to your Lighthouse CI config so the same commit that adds a route can assign its family.
# perf-baselines.yml — inheritance policy for new routes
families:
content-article:
match: "src/templates/article.njk"
percentile: p75
context: "moderate-mobile:fast-3g"
metrics: [largest-contentful-paint, interaction-to-next-paint, cumulative-layout-shift]
inheritance:
when_no_own_baseline: inherit-family-median
provisional_band_pct: 20
narrow_band_at:
- { min_sessions: 200, band_pct: 15 }
- { min_sessions: 500, band_pct: 12 }
promote:
min_field_sessions: 1000
window_days: 28
on: green-main
routes:
"/blog/2026-launch/": { family: content-article }
A small resolver reads that policy at gate time. It looks up the route's own baseline first and falls back to the family median, applying the correct band for the current session count.
// resolveBudget.js — returns the threshold a route is gated against right now
function resolveBudget(route, store, policy) {
const own = store.getOwnBaseline(route.url, route.metric);
const sessions = store.getSessionCount(route.url, route.metric, policy.promote.window_days);
if (own && sessions >= policy.promote.min_field_sessions) {
return { value: own, bandPct: 10, source: "own", provisional: false };
}
const family = store.getFamilyMedian(route.family, route.metric);
let bandPct = policy.inheritance.provisional_band_pct;
for (const step of policy.inheritance.narrow_band_at) {
if (sessions >= step.min_sessions) bandPct = step.band_pct;
}
const threshold = family * (1 + bandPct / 100);
return { value: family, threshold, bandPct, source: "inherited", provisional: true };
}
Verification
Before you trust the policy in CI, verify three things. First, confirm that a genuinely new URL resolves to its family and not to the global default — assert on the source: "inherited" field the resolver returns. Second, confirm the provisional band is actually applied, so a route landing 18 percent over the inherited 3100 ms LCP baseline at P75 on mid-range mobile over Fast 3G passes while one landing 40 percent over fails. Third, confirm promotion fires exactly once at N and never regresses back to provisional afterward.
# Verify a new route inherits and gates on the widened provisional band
node -e '
const { resolveBudget } = require("./resolveBudget.js");
const store = require("./mock-store.json");
const policy = require("js-yaml").load(require("fs").readFileSync("perf-baselines.yml","utf8"));
const r = resolveBudget({ url: "/blog/2026-launch/", metric: "largest-contentful-paint", family: "content-article" }, store, policy);
console.log(JSON.stringify(r, null, 2));
if (r.source !== "inherited") { console.error("FAIL: expected inherited"); process.exit(1); }
if (Math.round(r.threshold) !== 3720) { console.error("FAIL: band not applied, got " + r.threshold); process.exit(1); }
console.log("PASS: new route inherits family median at +" + r.bandPct + "% band");
'
Run this check in the same job that runs your regression gate, and log the resolved source, value, and bandPct for every URL in the build. That log is your audit trail: it shows at a glance which routes are still provisional, which have narrowed, and which have promoted — turning an otherwise invisible policy into something a reviewer can read in the CI output. Pair it with the percentile-selection guidance in Choosing Between P75 and P90 Budget Targets so the inherited context always matches the percentile the rest of your budget uses.
Frequently Asked Questions
Why inherit a baseline instead of just exempting new routes from the gate?
An exemption removes all protection at exactly the moment a route is most likely to ship a regression, and temporary waivers are routinely forgotten and become permanent. Inheritance keeps the route gated from day one against the defensible family median, so a genuinely slow launch still fails loudly.
How wide should the provisional tolerance band be?
Start at 20 to 25 percent over the inherited baseline, versus the 10 percent you use for established routes. On a family LCP baseline of 3100 ms at P75 on mid-range mobile over Fast 3G, a 20 percent band gates the new route at 3720 ms, which absorbs honest per-route variation while a 5200 ms regression still fails.
What value of N field sessions should trigger promotion?
Pick N large enough that the P75 you read is not dominated by a few outlier sessions — typically 500 to 1000 P75-qualifying sessions over a rolling 28-day window. Below that, keep inheriting but you can narrow the band as the count climbs.
Which family should a brand-new route inherit from?
The family is chosen by the template or layout that renders the route, not by URL prefix alone, so a new article inherits from the article family and a new dashboard from the dashboard family. This mirrors the classification used for per-template and logged-in-versus-anonymous segmentation.
Does inheritance replace computing a real per-URL baseline?
No. It is a temporary scaffold that holds until the route has its own field data, at which point promotion computes a real per-URL baseline exactly as Historical Baseline Calibration describes and the provisional flag is cleared.