== Changelog ==

= 1.2.3 =
* The recovery drill no longer writes into wp-content. It checks the panic constant, the secret panic URL, the safe-mode option and the autoload backups, and leaves the filesystem alone.
* The page profiler and the asset inventory stay on this site: a pasted URL on another host is refused, and scripts or styles served from another host are listed without a size instead of being fetched.
* The notice about an outdated Pro add-on appears on VelocityForge screens, the plugin list and the dashboard only.
* The usage-telemetry switch appears only where something honours it — the Pro add-on — instead of in every install.
* Request input is sanitised where it is read, database queries are prepared or documented, and admin output is escaped.

= 1.2.2 =
* **This release needs VelocityForge Pro 1.2.0 or newer.** An older add-on is switched off whole rather than left running: 1.2.0 is where the free screens started promising that a rule which begins applying is measured against real visitors for a day and reverted on its own, and every part of that decision is in the add-on — beside a 1.1.x add-on the sentence is printed and nothing measures anything. Nothing you have saved is touched, an admin notice names both versions and what stopped, and installing the current add-on brings all of it back.
* Deleting the last Forge product on a site now clears the job queue the Forge plugins share — its table, its two scheduled events and its version marker. While any other Forge product is still installed, deleting this one leaves the queue exactly as it is, because the pending jobs in those rows are that product's too.
* Admin script globals carry the full velocityforge prefix instead of spf.

= 1.2.0 =
* **New: a measurement now names what it points at.** An interaction is recorded with the control that was hit and with the time split three ways — the wait before the handler ran, the handler itself, and the repaint afterwards — plus whether the page was still loading at the time. A largest paint is recorded with its element and the file that element loaded. Those live in their own table and are counted rather than read one at a time, because "one visitor waited 340 ms on button.add-to-cart" is an anecdote and "this is the p75 of two hundred interactions, and three hundred of those milliseconds are the handler" is a task.
* Two cards on the Monitoring screen say it out loud: the ten controls that cost the most interaction time and the five elements that most delay the first paint, each with the file and, where it can be established, the plugin that shipped it. The owner is resolved from a prefix table, then from this site's own enqueued handles, then from a filter — and where none of the three answers it says "theme" or nothing, never a confident wrong name.
* Every figure is a 75th percentile per element over 28 days, and an element appears only once it has at least three measurements. A p75 of two measurements is the larger of two under a borrowed name.
* An LCP file is linked only when its host is your own site. Resource URLs arrive through an unauthenticated beacon, so a stranger's link is named but never made clickable.
* **New: the regression says what the update changed.** Immediately before an update replaces any file, VelocityForge fetches your real front page and reads its script and stylesheet queue out of the served HTML — the rendered page, not the handle registry — and afterwards the regression row names what the front end gained, lost or resized. A non-2xx response is refused before the body is parsed, because a bulk update serves a 503 maintenance page that parses perfectly into an empty list, which would have made your whole real asset queue look like an addition.
* That snapshot is bounded on purpose: at most twenty-four assets are sized per run, and sizing stops after a three-second budget, so updating twenty plugins cannot eat `max_execution_time` from inside the installer. Both the page fetch and the size probes go through the SSRF guard, because the URLs come out of somebody else's HTML.
* **New: the Database screen names owners.** The twenty heaviest autoloaded options are listed with the plugin that wrote each one, resolved from the option-name prefix. Read-only, as the free health report has always been.
* **New: the recovery drill (free).** One button runs four independent checks — the panic constant, a real loopback request to the secret panic route with a one-off secret, the safe-mode option's round trip, and the autoload backups table — and puts everything it touched back exactly as it found it, the stored secret included. A drill that left the secret changed would have broken the escape hatch it exists to verify.
* **New: Safe Mode in the admin bar**, on the front end as well as in wp-admin, because a broken site is usually noticed on the front end. It calls the same rollback manager the form does, so it lands in the same audit trail rather than becoming a second way to write the same state.
* **New in Pro: rules that take themselves back.** A rule that starts applying is measured for a window — 24 hours by default, settable 6 to 72 — against what real visitors measured the week before. If LCP or INP is worse by more than your threshold it is reverted through the same call your own button makes, the reason `auto-watch` is written onto the rollback event, and you get an e-mail with the numbers and how to put it back. It refuses on no licence, no beacon, too few measurements, Safe Mode, a hard-railed handle and a pinned rule; one watch is open per rule and one verdict is taken per watch, so a retried job cannot revert twice or send a second e-mail. The free plugin carries the table and announces that a rule was created or enabled; every decision is in the add-on.
* **Pro alerts are configurable by a person.** Bot tokens, webhook URLs and HMAC secrets used to sit in a JSON textarea in clear text and be printed straight back into it. They are now encrypted at rest and shown only as their last four characters, the screen is a card per channel with Add, Remove and a "Send test" that really sends and writes nothing, and an empty field means "leave it as it is" — so the first save through the new form re-encrypts an old clear-text value without you doing anything. The test path runs the same https-only, no-private-address guard as the save path, so it cannot be used to probe your internal network.
* **New: a Total Blocking Time budget**, 300 ms, in the same map as LCP, INP, CLS and TTFB. It is honest about itself: no field source populates it, because TBT needs a long-tasks trace the beacon does not capture, so it is read only by the Pro lab audits' comparison. Banding needed no new code — it keys off the metric name and does not care which instrument produced the value.
* The lab-audit panel's status line is now the run's own state instead of a fixed "Queued — refresh in a minute": nothing started says nothing, an open run says when it was queued, and a finished one repeats exactly the wording the history table uses, so the two can never disagree.
* Five more caching and optimisation plugins are recognised — Jetpack Boost, SiteGround Optimizer, WP-Optimize, Breeze and WP Fastest Cache — bringing the list to seventeen. The coexistence notice now names every feature we stood down rather than only the overlapping one: for a plugin that owns the whole asset pipeline the optimiser stands down entirely, and a banner claiming we had only stepped back from minification while four other features were silently off was worse than saying nothing.
* **New: the trend, on the Monitoring screen.** Thirty days of daily 75th percentile for LCP, INP, CLS and TTFB, so a regression row has the fortnight either side of it to be read against. A percentile rather than an average, because the average of a day where nine visitors waited a second and one waited eleven is two seconds, and two seconds is a number nobody experienced. A day with no measurements is a gap in the line, never a zero.
* Free, and it draws whatever it has. On a site with the Pro add-on and scheduled lab audits switched on, the same chart carries a second line.
* **New in Pro: scheduled lab audits.** Lighthouse runs against a list of your pages once a day through your own PageSpeed Insights key — your key, your Google quota, your billing relationship with Google, and no proxy of ours in between. The key is encrypted at rest with a value from wp-config.php and is never shown back to you.
* Lab results are ordinary measurements in the ordinary table, so the trend, the regression history, the e-mail and the Telegram / Slack / webhook alerts all treat them the same way. They are never compared against a real visitor's measurement, though: a reading from Google's edge and a reading from a browser in Jakarta are not the same instrument, and comparing them reports a regression whose only cause is the yardstick.
* **New: a Speculation panel.** WordPress 6.8 prefetches the next page on its own, conservatively and with no way to say "but not that link" short of writing a filter. This screen gives you the two controls — prefetch or prerender, and how eagerly — plus a list of paths to leave alone.
* It steers the rule set WordPress already prints rather than adding one of its own. That is not tidiness: a page may carry several speculation rule sets and the browser takes their union, so a second one can only add speculation, never take it away. "Prerender the site but never the cart" is unreachable any other way.
* Cart, checkout, my-account, logout, anything carrying a nonce and anything carrying `add-to-cart=` are excluded and cannot be switched on. Cart and checkout paths are read from the site — a shop whose cart lives at /basket/ is excluded at /basket — rather than guessed from slugs. Prefetch is railed as tightly as prerender, because a prefetch reaches the server too, and a speculated add-to-cart link puts a product in a real basket.
* The exclusions you write are checked and a bad line is refused, not repaired. One pattern the browser cannot read makes it discard every speculation rule on the page, so the screen drops the line and tells you how many it dropped.
* Defaults reproduce what WordPress does today — prefetch, conservative — so upgrading changes nothing about your pages until you move a control.
* On WordPress below 6.8 the panel says so plainly and the settings still save, ready for the upgrade. Nothing here is greyed out and nothing asks about a licence.

= 1.1.0 =
* Five things that were Pro are now Free, because their code was in this download all along. WordPress.org rejected a plugin like ours in August for exactly that — a licence check standing on top of code the free user already has — and it was right to. What moved:
  * **LCP hints.** `fetchpriority="high"` on the hero image and the matching `<link rel="preload">`. WordPress core has set fetchpriority on its own LCP guess since 6.3, so this was a charge for the platform.
  * **Field-data retention.** Free was clamped to seven days, which is too short to see a weekly pattern twice. It is thirty by default now, and will honour up to ninety.
  * **Batch scans.** Twenty-five URLs free, two hundred paid. There is now one cap, fifty, for everybody: it exists so a shared host survives a batch of synchronous self-requests, and that reason does not care who paid.
  * **The regression e-mail.** It was repaired in 1.0.1 and sold as Pro in the same release. Charging for a fix is not a business model. Detection was already free; being told is free too.
  * **The database health report.** Reading how much your site autoloads was never worth money.
* What Pro is now, stated plainly: the optimiser (asset rules, critical CSS, WebP/AVIF conversion, autoload and transient cleanup) as a single purchase rather than three separate keys; regression alerts delivered to Telegram, Slack or a webhook; a year of field data with CSV export; and the AI advisor. Every one of them is code that ships in the add-on.
* No screen in the free plugin greys a control out because you have not paid. Where a feature lives in the add-on, the page says so in a sentence and shows you the part that is free — the asset inventory, the autoload table, the health numbers, the regression history.
* Existing Pro licences are unaffected: the three optimiser keys became one, and everything they covered stays included.
* Every field measurement now records what it points at — the LCP element, the interaction target, the node that shifted, and for an interaction the split between input delay, processing and presentation. VelocityForge has always downloaded the build of web-vitals that carries this; it used to throw it away on arrival.
* **Fixed: no schema change had ever reached a site that already had the tables.** Every migration was written as `CREATE TABLE IF NOT EXISTS`, and dbDelta reads the table name straight after `CREATE TABLE` — so it believed the table was called `IF`, found none, and compared no columns, while the stored version number recorded a migration that had not happened. Corrected, and a test now fails on any migration written that way.

= 1.0.2 =
* The Core Web Vitals beacon now measures. The bundled copy of Google's web-vitals library had never been vendored — a placeholder file shipped in its place, so the measuring script was never defined and every field-data screen stayed empty on every install. web-vitals 4.2.4 is now bundled for real. If your beacon has been on and showing nothing, this is why.
* Removed "Scheduled CWV Monitoring" from the Pro feature list. No recurring audit was ever implemented behind it: the licence key unlocked nothing, so it should not have been sold. Post-update regression detection is unaffected — it is free, always on, and continues to fill the Monitoring screen. Regression e-mails remain Pro.
* Removed a daily cron left behind by that feature. It had no handler, so it woke wp-cron once a day to do nothing; existing sites are cleaned up automatically on upgrade.
* Translations caught up with the plugin. The catalogue had not been regenerated since 1.0.0, so the regression e-mail, the field-data export screen and the rewritten Monitoring notice all rendered in English on the fifteen translated locales. All fifteen are complete again, and Arabic, Hindi and Chinese now declare their plural rules instead of borrowing English ones.

= 1.0.1 =
* Regression alerts actually send. The "Email me on regressions" toggle had existed since launch with nothing behind it.
* Field-data CSV export, streamed — the half of "90-day retention" that was advertised and missing.
* Removed two licence entries with no code behind them (per-page profiles was a second name for asset rules; agency reports was not built).

= 1.0.0 =
* Initial public release. WordPress.org Free core; Pro features delivered by the separate VelocityForge Pro add-on.
