Skip to content

feat(warmup): a curated warmup list that lives in code, not in wp-admin #140

Description

@parisek

Follow-up to #134. The scorer already has manual as a source at weight 800, but the only way to feed it is Breeze's own settings row. That makes the one curated signal in the model the one signal a project cannot version.

The problem

SignalCollector::manualKeys() reads breeze-preload-cache-urls out of breeze_get_option( 'preload_settings' ). So a curated list:

  • is not in git — it cannot be reviewed, and nothing records why a URL is on it
  • is wiped by a database import, silently, and the site quietly stops warming what mattered
  • has to be re-entered per environment, and differs between them by accident rather than intent

Measured on sloneek while adopting #134: the site's 53 category archives scored only through the manual list. They are in no menu, are not a homepage, and their lastmod is old — so with that row emptied they fell from position 27-30 to ~2864 of 2873. That project then decided the archives are not priority, which resolved it there, but the shape of the problem is general.

The signals the kit has are homepage, menu membership and freshness. A priority page that is none of those has no way up:

  • a campaign landing page deliberately not in a menu
  • a deep page carrying real search traffic
  • a category archive on a project where it is priority
  • anything correct and finished, so its lastmod is old

Proposed shape

protected array $breeze_warmup_urls = array(
    '/blog/',
    '/cs/blog/',
    'https://example.de/blog/',
);

Entries merge into manualKeys(), so they score at the existing manual weight and need no new weight, no new tier and no ordering change.

Both forms accepted, deliberately. A relative path is what a project wants most of the time — the same code runs on ddev, on staging and on production, and an absolute URL would be silently wrong on two of the three (rejected by is_local_url(), warming nothing, reporting nothing). An absolute URL is what a project needs when the target is not on home_url()'s host at all.

Which is the case with domain-per-language

isFetchableSameHostUrl() compares against home_url()'s host and nothing else. Under WPML's domain-per-language negotiation every non-default language is on a different host, so an absolute URL for one of them is rejected today.

SignalCollector already calls wpml_active_languages, which carries each language's url, so the allowed-host set is derivable. This feature needs it, and the sitemap crawl arguably wants the same treatment — though that is a separate change and should not ride along silently.

Open question

Whether a relative path should be expanded across active languages, or taken literally. Literal is more predictable and matches what is written; expansion saves five lines per entry on a five-language site and removes the chance of forgetting one. My inclination is literal, with expansion left to a filter, but it is worth deciding before the API exists rather than after.

Not proposed

Any change to ordering, weights or the cap. This adds an input to a source that already exists.

🤖 Generated with Claude Code

https://claude.ai/code/session_01JMYy6JHLf4mU4H4Hd47spb

Metadata

Metadata

Assignees

Labels

No labels
No labels

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions