= 3.02.02 =
* Fixed the LiteSpeed durable invalidation queue aggregate SQL to pass the `tag:` LIKE wildcard through `$wpdb->esc_like()` and prepared `%s` placeholders instead of embedding the wildcard in the query string. Queue counts and matching semantics are unchanged.
* Documented the three guarded LiteSpeed queue commit UPDATEs with narrow WordPress Coding Standards annotations. These operations intentionally use direct atomic SQL against the UltraCache-owned durable queue so claim-token, lease, requeue, success, retry, and terminal-error transitions remain one guarded database mutation; no object-cache layer was added around mutable queue state.
* Documented the six intentional `curl_setopt()` calls used only inside the scoped `http_api_curl` transport hook. UltraCache continues to execute requests through `wp_safe_remote_request()` / `wp_remote_request()`; the low-level options only preserve the existing PHP execution-time contract, including truly unlimited transport timeout when `max_execution_time=0`. No arbitrary timeout or raw cURL request path was introduced.
* No LiteSpeed queue semantics, retry policy, Varnish behavior, media rewriting, response-cookie behavior, WooCommerce routing, cache schema, or runtime timeout policy changed.
* Files changed:
  - `ultracache.php`
  - `README.md`
  - `readme.txt`
  - `changelog.txt`
  - `includes/core/http-guards.php`
  - `includes/integrations/litespeed/class-litespeed-queue-trait.php`

= 3.02.01 =
* Moved the floating **Rate us** badge slightly lower and further left on desktop (`top: 75px; right: 50px`) while preserving the existing responsive mobile override.
* Fixed Slider Revolution 7 runtime image replacement for UltraCache-generated WebP/AVIF HTML buckets without rewriting Slider Revolution scripts or serialized JSON. Slider Revolution keeps original image identity in `data-dbsrc` and resolves those runtime references through the hidden `<image_lists>` replacement map. UltraCache now reconciles that map after the normal bucket-aware media rewrite: `data-src` uses the existing best-format resolver, an existing `data-src2` is independently optimized when possible, and when Slider Revolution provides no `data-src2` the resolved optimized `data-src` is mirrored into it. This prevents Slider Revolution's device-pixel-ratio path from falling back to the original JPG/PNG while preserving `data-dbsrc` unchanged as the canonical lookup identity.
* Kept the Slider Revolution integration format-agnostic. The same existing UltraCache resolver selects AVIF when the active cache bucket/Accept context supports AVIF and a valid AVIF variant exists, otherwise WebP when available; there is no `.jpg -> .webp` string replacement and no hardcoded site/image rule.
* Fixed UltraCache-generated LCP image preloads inside optimized HTML buckets. Final media reconciliation now recognizes only `<link rel="preload" as="image" data-ultracache-lcp-preload="1">`, rewrites its `href` through the same bucket-aware media resolver, and updates `type` to the resulting MIME (`image/webp` or `image/avif`). Ordinary link tags and non-UltraCache preloads are unchanged.
* Preserved Slider Revolution runtime JSON/PMH data exactly. This release does not globally rewrite `<script>` contents, escaped JSON URLs, `SR7.JSON`, `SR7.PMH`, `slides[]`, `layers[]`, or `data-dbsrc`; those original URLs remain the identities Slider Revolution uses to match the optimized `<image_lists>` entries.
* No LiteSpeed, Varnish, cookie-handoff, WooCommerce routing, queue schema, cacheability policy, transient, raw HTTP client, or execution-deadline behavior changed.
* Files changed:
  - `ultracache.php`
  - `README.md`
  - `readme.txt`
  - `changelog.txt`
  - `includes/media/class-media-html-rewrite-trait.php`
  - `includes/admin/css/sections.css`

= 3.01.10.6 =
* Closed the 3.01.10 dependency-aware warm/refill roadmap with a cross-cache regression pass rather than adding another cache architecture. Live production verification completed the public shared-cache handoff on both supported outer-cache stacks: Varnish `orig` / `webp` / `avif` variants completed incidental `_fbp` handoff as MISS(cookie) -> MISS(clean) -> HIT, while LiteSpeed `orig` / `webp` variants completed incidental `PHPSESSID` handoff as BYPASS(cookie) -> clean refill -> HIT. Private/auth/cart response-cookie rejection remains owned by Cacheability Policy v2.
* Corrected the remaining Varnish refill diagnostic cosmetic exposed by the live handoff proof. When warm, clean-handoff, and verification request histories are merged, `attempts[]` now reports one monotonic 1/2/3/... timeline instead of preserving each helper call's local `attempt: 1`. Request execution, retry behavior, cookie classification, refill accounting, and HIT verification are unchanged.
* Re-audited the cross-cache contracts carried through 3.01.10.x: no operational hardcoded `/cart/`, `/checkout/`, or `/my-account/` route remains in cache-control code or bundled VCL; the removed global `X-UltraCache-Cacheable: 1` Varnish permission gate remains absent; Varnish and LiteSpeed refill classifiers both consume canonical runtime settings; and cookie handoff never strips, rewrites, persists, or replays response-cookie values. UI placeholders/examples that mention conventional WooCommerce paths are documentation only.
* Preserved generic Varnish behavior. The bundled VCL was not changed in this closeout release, UltraCache-specific response headers remain optional enhancements rather than baseline Varnish prerequisites, and the previously live-verified exact BAN, batch BAN, HTML-only flush, WooCommerce private subtree bypass, variant refill, product-update refill, and final Varnish HIT contracts remain untouched.
* Preserved LiteSpeed native behavior. Semantic Smart Tags, signed internal exact invalidation, per-variant MISS/HIT verification, durable invalidation, refresh-ahead, and the anonymous public cookie-handoff path are unchanged.
* Historical terminal queue rows are deliberately retained as durable diagnostic evidence in this closeout. This release does not auto-delete or reinterpret old failures after a later capability recovery; current-vs-historical outcome presentation belongs to the 3.01.11 Verification & Performance metrics work.
* No VCL, WooCommerce routing contract, response-cookie classifier, page-cache eligibility rule, queue schema, transient, raw HTTP client, or arbitrary execution deadline changed.
* Files changed:
  - `ultracache.php`
  - `README.md`
  - `readme.txt`
  - `changelog.txt`
  - `includes/integrations/varnish/class-varnish-refill-trait.php`

= 3.01.10.5.2 =
* Fixed the Varnish clean cookie-handoff request introduced in 3.01.10.5. The initial refill remains an authenticated UltraCache warm request so it can populate the page cache without recursive first-visit work, but the follow-up handoff and verification requests are now ordinary anonymous public GETs. This lets `advanced-cache.php` serve the newly stored UltraCache object before WordPress/plugin code can emit the same incidental response cookie again, allowing Varnish to receive and store a genuinely cookie-free public representation.
* Kept runtime-control credentials out of the clean shared-cache handoff. `X-UltraCache-Token`, `X-UltraCache-Internal-Request`, and `X-UltraCache-Warm` are sent only by the authenticated first-stage warm request; the public handoff/verification requests carry only normal anonymous refill headers. No VCL change is required because the Varnish cache contract does not depend on these UltraCache internal-control headers.
* Corrected Varnish refill accounting for an incomplete cookie handoff. A variant that required a clean handoff but still carried an allowed response cookie is no longer included in `refilledCount` until a cookie-free handoff request has completed. The HTTP request may remain a successful warning result for diagnostics, but it is not reported as a populated shared-cache variant.
* LiteSpeed refill logic is unchanged in this hotfix because its handoff requests were already anonymous public requests and do not carry the Varnish runtime-control token.
* No response cookie is removed, rewritten, persisted, or replayed. No VCL, BAN/PURGE transport, capability detector, WooCommerce routing contract, page-cache cookie classifier, transient, custom table, raw HTTP client, or arbitrary execution deadline changed.
* Changed files: ultracache.php; README.md; readme.txt; changelog.txt; includes/integrations/varnish/class-varnish-refill-trait.php.

= 3.01.10.5.1 =
* Fixed Cookie-aware Shared Cache Handoff settings wiring in both Varnish and LiteSpeed refill paths. The Policy v2 classifier now receives canonical runtime settings, so an enabled **Cache Public Pages with Response Cookies** setting is no longer misread as strict mode during outer-cache refill.
* Corrected Varnish refill accounting when response-cookie policy explicitly blocks shared-cache eligibility. HTTP 200/MISS transport completion is no longer counted as a refilled HTML variant in that case; diagnostics now expose `sharedCacheBlocked` and report the actual eligible refill count.
* No VCL changes. No response cookies are removed, rewritten, persisted, or replayed by this hotfix.
* Changed files: ultracache.php; README.md; readme.txt; changelog.txt; includes/integrations/varnish/class-varnish-refill-trait.php; includes/integrations/litespeed/class-litespeed-refill-trait.php.

= 3.01.10.5 =
* Added the roadmap's cookie-aware shared-cache handoff without changing Cacheability Policy v2 page eligibility. The canonical response-cookie classifier is now shared by page storage, Varnish refill, and LiteSpeed refill, so private/auth/cart response cookies remain hard rejects while incidental/public response cookies such as `_fbp` can keep the HTML page public without teaching each outer-cache integration a separate cookie list.
* Split page cacheability from representation-level shared-cache readiness. A public page that emits an allowed `Set-Cookie` is still stored by UltraCache, but that cookie-bearing HTTP representation is explicitly marked `X-UltraCache-Shared-Cache-State: deferred-response-cookie` and is not offered to Varnish/LSCache as a public object. The visitor still receives the real `Set-Cookie`; UltraCache never strips, stores, or replays the cookie value. Clean public responses advertise `ready`, while true private/no-store responses remain forbidden.
* Added a bounded clean handoff to Varnish refill. When the first refill response is a successful UltraCache public STORE/HIT carrying only Policy-v2-allowed response cookies, UltraCache sends one clean follow-up request for the same HTML bucket. If Varnish exposes a MISS on that clean request, one optional third request verifies HIT/STALE. Generic Varnish remains supported: UltraCache-specific headers are diagnostics/hints only, and lack of observable Varnish HIT headers does not turn a successful clean HTTP handoff into a transport failure.
* Extended LiteSpeed refill for the corresponding cookie edge case. A first LSCache BYPASS caused only by an allowed public response cookie is treated as a deferred handoff rather than a private-page failure; UltraCache then performs the clean request needed to let LSCache store the object and, when that clean request is a MISS, one final request verifies HIT. Explicit `X-LiteSpeed-Cache-Control: no-cache` now correctly overrides a simultaneous `X-LiteSpeed-Cache: miss`, so a genuinely private/no-cache response stops after one request instead of entering normal MISS verification. The existing fast path is unchanged: an already-HIT bucket still costs one request, and an ordinary cacheable MISS without a cookie still uses the existing MISS -> HIT two-request verification.
* Added bounded diagnostics to refill details (`cookieHandoffRequired`, `cookieHandoffCompleted`, cookie-policy reason, cookie names only, request count, and optional verification status). Cookie values are never persisted or exposed by these diagnostics.
* No Varnish VCL, Varnish capability canary, BAN/PURGE transport, WooCommerce routing contract, page-cache private-cookie rules, transient, custom table, raw HTTP client, or arbitrary execution deadline changed in this release.
* Files changed:
  - `ultracache.php`
  - `README.md`
  - `readme.txt`
  - `changelog.txt`
  - `includes/core/request-policy.php`
  - `includes/engine/class-engine-cache-decision-trait.php`
  - `includes/engine/class-engine-response-headers-trait.php`
  - `includes/integrations/varnish/class-varnish-refill-trait.php`
  - `includes/integrations/litespeed/class-litespeed-refill-trait.php`

= 3.01.10.4.1 =
* Fixed repeated PHP 8+ `Undefined array key 5` warnings in generated-asset reference extraction during page storage/warm operations. The HTML attribute parser now treats the unquoted-value capture as optional instead of indexing capture group 5 when a quoted `href` or `style` value caused the match array to end earlier.
* Applied the same bounded capture handling to both generated-asset extraction paths that use the identical attribute regex, preventing the warning from recurring for either stylesheet-link `href` parsing or inline `style` attribute parsing. Empty/unmatched values remain ignored exactly as before; generated CSS reference collection behavior is otherwise unchanged.
* This is a storage-parser warning hotfix only. No Varnish VCL, Varnish capability detection, BAN/PURGE logic, warm/refill policy, WooCommerce routing contract, LiteSpeed behavior, response-cookie policy, transient, custom table, raw HTTP client, or execution deadline was changed.
* Files changed:
  - `ultracache.php`
  - `README.md`
  - `readme.txt`
  - `changelog.txt`
  - `includes/engine/class-engine-storage-trait.php`

= 3.01.10.4 =
* Fixed the Varnish regression introduced by the 3.01.10.2 CWP template change. The bundled VCL no longer requires `X-UltraCache-Cacheable: 1` as a global permission token before storing every dynamic/non-asset response. That gate was stricter than the already-proven generic Varnish integration, prevented the existing public capability canary from being retained, and unnecessarily converted an UltraCache-specific diagnostic header into a prerequisite for normal Varnish caching.
* Restored the previously verified generic Varnish response contract: ordinary dynamic responses are cacheable according to standard origin HTTP semantics (`Cache-Control` / `s-maxage`, `Set-Cookie`, `Pragma`, `Vary`, status, and the existing VCL policy). `X-UltraCache-*` headers remain available for enhanced UltraCache integrations and diagnostics but are not required for baseline public-object storage.
* Kept the 3.01.10.2 dynamic WooCommerce routing work without reintroducing hardcoded `/cart/`, `/checkout/`, or `/my-account/` paths into VCL. Request-visible WooCommerce actions such as `add-to-cart`, `wc-ajax`, and `wc-api` still PASS immediately, while the dynamically resolved private WooCommerce pages continue to emit the normal `private` / `no-store` origin response policy that Varnish already respects.
* Intentionally did not change response-cookie classification or the `_fbp`/incidental `Set-Cookie` shared-cache handoff in this release. Cookie-aware shared-cache optimization remains its separate roadmap item so the VCL regression fix stays independently testable and regression-traceable.
* No Varnish capability detector, canary response, purge/BAN implementation, warm/refill pipeline, WooCommerce routing contract, transient, custom table, raw HTTP client, or arbitrary execution deadline was changed.
* Files changed:
  - `ultracache.php`
  - `README.md`
  - `readme.txt`
  - `changelog.txt`
  - `resources/varnish/control-web-panel/ultracache-cwp-varnish.tpl`

= 3.01.10.3 =
* Fixed a 3.01.10.2 early-bootstrap regression where WP-CLI or full admin/CLI drop-in reconciliation could call `wc_get_page_permalink()` from `plugins_loaded` before WordPress had initialized the global `WP_Rewrite` object, causing a fatal `Call to a member function get_page_permastruct() on null`.
* Added an explicit WordPress rewrite-runtime capability check to the modern WooCommerce routing contract builder. The builder is now safe at every bootstrap phase: before rewrite initialization it returns an unavailable fail-closed observation and never calls WooCommerce permalink helpers.
* Changed the shared WooCommerce contract getter so early `plugins_loaded`/WP-CLI bootstrap reuses the last persisted dynamic contract rather than attempting live permalink resolution. This keeps the last known private Cart/Checkout/My Account paths excluded until normal WordPress routing bootstrap is ready. A first install with no persisted contract remains fail closed instead of guessing default WooCommerce paths.
* Guarded contract synchronization itself against early invocation so a temporary `wordpress-rewrite-runtime-unavailable` observation can never be persisted as the routing source of truth. Normal `admin_init`/shutdown synchronization still resolves the live modern WooCommerce contract and rebuilds the embedded/server layers after WordPress routing initialization.
* Preserved the 3.01.10.2 cross-subsystem dynamic endpoint contract, Varnish/LiteSpeed behavior, clean-slate routing-change boundary, and no-WooCommerce-8 compatibility policy. No transient, new custom table, raw HTTP client, or arbitrary execution deadline was added.
* Files changed:
  - `ultracache.php`
  - `README.md`
  - `readme.txt`
  - `changelog.txt`
  - `includes/integrations/woocommerce/functions.php`
  - `includes/runtime/class-runtime-config-trait.php`

= 3.01.10.2 =
* Replaced hardcoded WooCommerce `/cart/`, `/checkout/`, `/my-account/`, and endpoint-path assumptions with one dynamic WooCommerce routing contract built from the current stable `wc_get_page_id()` / `wc_get_page_permalink()` / `wc_get_endpoint_url()` APIs plus `WC()->query->get_query_vars()` metadata. UltraCache intentionally does not add a WooCommerce 8.x compatibility resolver or legacy path guesses.
* The contract resolves the actual Cart, Checkout, and My Account public page URLs and treats each base page as the authoritative private/dynamic subtree. Custom routes such as `/basket/`, `/payment/`, or `/pelates/` therefore propagate automatically to cacheability inspection, warm eligibility, early cache delivery, server rules, and external-cache automation. When Checkout or My Account is assigned to the front page, `/` remains an exact exclusion and the configured WooCommerce endpoint slugs are also materialized as root-level dynamic paths.
* Added persistent `ultracache_woocommerce_endpoint_contract_v1` metadata with separate observed/full, routing, and successfully-applied fingerprints. Admin requests compare the live modern WooCommerce contract with the installed snapshot; WooCommerce page/endpoint setting changes and slug/permalink updates coalesce into one shutdown synchronization. Metadata-only changes update without cache churn, while actual routing changes rebuild the embedded/runtime server contracts. Failed filesystem/server-rule synchronization leaves the applied fingerprint behind so a later admin request retries instead of recording false success, including the WooCommerce-deactivation path.
* Made WooCommerce routing changes a clean-slate public-cache boundary. Before installing changed early-cache/server rules, UltraCache purges the current page/external cache layers so a URL that was previously public cannot remain as a stale UltraCache, LiteSpeed, or Varnish object after becoming Cart, Checkout, My Account, or one of their dynamic endpoint paths.
* Embedded the resolved WooCommerce dynamic paths into `advanced-cache.php`; the early drop-in no longer contains literal WooCommerce page/endpoint paths and can reject custom Cart/Checkout/My Account URLs before WordPress/WooCommerce bootstrap. If WooCommerce is active but the modern routing APIs are unavailable, or plain permalink routing cannot be represented safely by the path-only early/server layers, UltraCache fails closed instead of guessing legacy paths.
* Updated managed LiteSpeed and Apache static-delivery rules to consume the same dynamic path contract. The shared path regex also handles a WooCommerce dynamic base page assigned to `/` without turning every descendant URL into a false root match.
* Extended Varnish Refresh Ahead with the same authoritative warm/cacheability preflight already used by LiteSpeed Refresh Ahead. Custom WooCommerce/private/excluded URLs are now recorded as `excluded`, do not consume probe budget, do not increment the error counter, and are never probed or queued for Varnish refill. Varnish invalidation/refill normalization continues to use `inspect_url()`, which now consumes the same dynamic WooCommerce path contract.
* Removed hardcoded WooCommerce route names from the bundled Control Web Panel Varnish template as well. The generic CWP VCL now fails closed for dynamic/non-asset responses and stores them only when UltraCache explicitly emits `X-UltraCache-Cacheable: 1`; WooCommerce action query arguments and customer/session cookies still PASS at request time. This keeps the shipped Varnish configuration independent of custom Cart/Checkout/My Account slugs instead of pretending a static route list is authoritative.
* Updated WordPress Speculation Rules exclusions and third-party iframe lazy-load request protection to resolve WooCommerce dynamic pages from the central contract rather than literals. The WooCommerce ESI mini-cart fallback now resolves the current Cart page through the same contract instead of `wc_get_cart_url()` plus a hardcoded `/cart/` fallback.
* Removed WooCommerce paths from fresh-install manual exclusion defaults. Existing administrator-maintained exclusion lists are preserved; the dynamic routing contract is now the independent source of truth for WooCommerce private pages.
* Added uninstall/plugin-data cleanup for the persistent WooCommerce routing contract. No transient, new custom table, raw HTTP client, arbitrary execution deadline, or WooCommerce 8.x compatibility branch was added.
* Files changed:
  - `ultracache.php`
  - `README.md`
  - `readme.txt`
  - `changelog.txt`
  - `uninstall.php`
  - `templates/advanced-cache-template.php`
  - `includes/bootstrap/class-bootstrap-trait.php`
  - `includes/engine/class-engine-cache-decision-trait.php`
  - `includes/engine/class-engine-html-output-trait.php`
  - `includes/engine/class-engine-iframe-lazy-load-trait.php`
  - `includes/integrations/varnish/class-varnish-refresh-ahead-trait.php`
  - `includes/integrations/woocommerce/class-woocommerce-esi-trait.php`
  - `includes/integrations/woocommerce/functions.php`
  - `includes/lifecycle/class-plugin-data-cleanup-trait.php`
  - `includes/runtime/class-runtime-config-trait.php`
  - `includes/server/class-server-rules-trait.php`
  - `includes/settings/class-settings-registration-trait.php`
  - `resources/varnish/control-web-panel/ultracache-cwp-varnish.tpl`

= 3.01.10.1 =
* Fixed LiteSpeed Refresh Ahead candidate eligibility so sitemap/menu discovery no longer treats known private or excluded pages such as WooCommerce `/my-account/` as queue failures. Refresh Ahead now reuses the shared warm-pipeline preflight, which in turn reuses the authoritative anonymous page-cache `inspect_url()` decision for WooCommerce dynamic paths, configured exclusions, and query policy. Non-cacheable candidates are recorded as `excluded`, do not consume the per-scan cacheable-page budget, and do not increment the error counter.
* Unified targeted/full-site page-warm CSS accounting with the existing **Also Warm up CSS** setting. When `warmCssBundlesEnabled` is enabled and the CSS bundle feature is active, newly queued ordinary `page_warm` rows now declare `css_bundle` in `required_stages` from the start. The worker therefore records CSS in `completed_stages`, retries/failure accounting can see the CSS stage, and messages such as `Cached + CSS` no longer describe work that is absent from the persistent stage contract.
* Preserved the real 3.01.10 dependency-aware LiteSpeed flow verified on pharmaplot: all exact and semantic invalidations drain before affected page warming, semantic tag purges remain durable, and verified `orig` / `webp` refill behavior is unchanged. No Smart Tag, `.lstags`, purge syntax, or refill transport behavior was changed.
* Reused existing WordPress/UltraCache APIs and persistent state only. No transient, new table, custom HTTP client, arbitrary execution deadline, or site-specific Booked/WooCommerce exception was added.
* Files changed:
  - `ultracache.php`
  - `README.md`
  - `readme.txt`
  - `changelog.txt`
  - `includes/integrations/litespeed/class-litespeed-refresh-ahead-trait.php`
  - `includes/warmup/class-cron-warm-orchestrator-trait.php`
  - `includes/warmup/class-warm-page-pipeline-trait.php`

= 3.01.10 =
* Added dependency-aware LiteSpeed warm ownership for affected content changes. When the affected-page rebuild pipeline is already enabled and has cacheable warm URLs, the `ultracache_after_purge_urls` LiteSpeed invalidation still persists and drains first for correctness but no longer adds a second independent refill request. The shared affected-page warm row becomes the single authoritative HTML/CSS/LiteSpeed rebuild owner. If affected-page warming is disabled/unavailable, or if the shared warm queue fails to accept an eligible URL after ownership was assigned, the existing persistent LiteSpeed invalidation row is upgraded back to refill ownership so cache coverage is not lost.
* Removed a redundant destructive purge from LiteSpeed refresh-ahead. A refresh-ahead worker already performs its stale exact-URL purge during the claim-time freshness recheck; the later LiteSpeed refill stage now receives an explicit `litespeed_invalidation_completed` contract and does not issue a second hard exact purge. This preserves stale-while-regenerating behavior and reduces signed control loopbacks.
* Made production LiteSpeed refill verification evidence-based. A bucket that is already `HIT` completes with one request. A cold bucket that returns `MISS` receives exactly one follow-up request; only `MISS -> HIT` is recorded as a verified stored object. This avoids blind duplicate requests on hot variants while proving that newly populated `orig` / `webp` / `avif` objects actually committed to LSCache.
* Kept operational compatibility for origins that hide cache headers or cannot expose a follow-up HIT. A successful HTTP refill without stored-HIT evidence remains a completed result with an explicit warning instead of being silently promoted to verified. Per-bucket details now retain `verified`, `requestCount`, `initialCacheStatus`, and `verificationCacheStatus`, and the aggregate refill reports `verifiedCount` / `verified`.
* Changed LiteSpeed refresh-ahead scheduling so `queued` is no longer treated as equivalent to `warmed`. Queue acceptance now keeps the candidate on the normal scan cadence, while the existing persistent pending-row guard prevents duplicate enqueue. Only the authoritative warm worker may advance a successfully verified candidate by the refresh threshold.
* Upgraded the persistent LiteSpeed refresh candidate registry to schema version 2 in the existing non-autoloaded WordPress option. Each candidate can now retain `lastWarmAt`, `lastWarmResult`, `lastWarmVerified`, `warmCount`, and the successfully contacted HTML buckets. No transient or new table was introduced.
* Added persistent refresh-ahead completion evidence to the existing state option: verified/unverified warm counters plus last warm URL/time/result. Worker completion updates this state only after the queue row result is authoritatively committed; lease loss, retry, or rerun never masquerades as a completed warm.
* Extended duplicate-work avoidance: LiteSpeed refresh-ahead now treats an exact URL with either a pending/processing shared `page_warm` row or a pending/processing `litespeed_invalidate` row as already owned. Refresh discovery therefore does not enqueue a competing page job while correctness invalidation is still pending.
* Preserved the 3.01.09.2 Smart Tags, per-variant behavior diagnostics, native tag syntax, `.lstags` metadata, durable invalidation ordering, and WordPress HTTP API transport. No custom wall-clock timeout, raw HTTP client, or transient was added; request execution continues to follow PHP `max_execution_time` and existing persistent coordination leases.
* Files changed:
  - `ultracache.php`
  - `README.md`
  - `readme.txt`
  - `changelog.txt`
  - `includes/integrations/litespeed/class-litespeed-control-trait.php`
  - `includes/integrations/litespeed/class-litespeed-refill-trait.php`
  - `includes/integrations/litespeed/class-litespeed-refresh-ahead-trait.php`
  - `includes/integrations/litespeed/class-litespeed-refresh-candidates-trait.php`
  - `includes/warmup/class-affected-url-coalescing-trait.php`
  - `includes/warmup/class-cron-warm-orchestrator-trait.php`
  - `includes/warmup/class-warm-page-pipeline-trait.php`

= 3.01.09.2 =
* Fixed the remaining LiteSpeed behavior-test false failure for multi-variant HTML caches. Live pharmaplot verification proved the WebP vary object stores correctly as MISS -> HIT when tested independently, while the combined diagnostic could report orig PASS followed by WebP MISS -> MISS. The behavior test now gives each active `orig` / `webp` / `avif` bucket its own exact purge -> MISS -> HIT contract instead of sharing one purge across sequential vary-bucket verification.
* Kept the 3.01.09 Smart Tag and production warm contracts unchanged. This hotfix changes only diagnostic sequencing; semantic tag names, `.lstags` metadata, native purge syntax, durable invalidation, public refill request headers, and the real warm pipeline are untouched.
* Preserved cross-process diagnostic isolation from 3.01.09.1. The shared persistent execution fence remains held and renewed around every per-bucket purge and request, so durable invalidation and refresh-ahead cannot interleave with an individual bucket verification.
* Added a final no-purge restore pass for every active LiteSpeed HTML bucket. Because an exact URL tag purge intentionally evicts every vary variant, the diagnostic now repopulates all active variants after independent verification instead of leaving only the last-tested bucket warm.
* No transient, new table, arbitrary execution deadline, or raw HTTP client was added. All diagnostic requests continue through the existing WordPress HTTP API / UltraCache trusted-loopback path and actual execution continues to follow PHP `max_execution_time`.
* Files changed:
  - `ultracache.php`
  - `README.md`
  - `readme.txt`
  - `changelog.txt`
  - `includes/integrations/litespeed/class-litespeed-diagnostics-trait.php`

= 3.01.09.1 =
* Fixed a LiteSpeed behavior-diagnostic race exposed after durable invalidation and refresh-ahead became persistent background work. The diagnostic now acquires the same persistent atomic cron/warm mutex and authoritative execution fence used by the real background worker before it starts the preflight -> exact purge -> MISS -> HIT sequence, preventing a second queued purge from landing between the two post-purge requests and producing a false MISS -> MISS failure.
* Kept the 3.01.09 Semantic Smart Tags contract unchanged. Live verification on the tagged UltraCache early-HIT path already proved `X-LiteSpeed-Tag` plus `X-LiteSpeed-Cache-Control: public,max-age=...` stores correctly as LiteSpeed MISS -> HIT, so this hotfix changes only diagnostic isolation and does not alter tag names, `.lstags` metadata, purge syntax, or durable semantic invalidation.
* Reused the existing UltraCache persistent lock/decision infrastructure; no transient, new table, or parallel diagnostic lock subsystem was added. The lock TTL remains a crash-recovery/coordination lease rather than an execution deadline, while actual HTTP work continues to follow PHP `max_execution_time` and the existing WordPress HTTP API contract.
* Renewed the shared execution fence between behavior-test stages. If another owner already holds the warm/invalidation fence, the test now returns an explicit inconclusive/deferred result without purging. If ownership changes unexpectedly during the test, the diagnostic stops and records inconclusive rather than converting a concurrency event into a cache failure.
* Files changed:
  - `ultracache.php`
  - `README.md`
  - `readme.txt`
  - `changelog.txt`
  - `includes/integrations/litespeed/class-litespeed-diagnostics-trait.php`

= 3.01.09 =
* Added native LiteSpeed **Semantic Smart Tags** on top of the existing site and exact-URL tag contract. Cacheable WordPress responses can now carry site-namespaced semantic identities for singular posts/pages/products, post-type archives, taxonomy term archives, author archives, date archives, the front page, the posts index, and the WooCommerce shop archive. Semantic tags are namespaced from the stable UltraCache site identity so multisite or neighboring WordPress caches do not share generic `post 123` / `term 42` tag names.
* Added narrow semantic dependency invalidation for WordPress content changes. Post changes now enqueue the changed singular object plus relevant post-type archive, author, date, assigned public-taxonomy terms, front/posts-index dependencies, and WooCommerce shop dependency where applicable. Term create/edit/assignment changes enqueue the matching term dependency. Existing exact affected-URL invalidation remains active as the correctness fallback while semantic tags reduce the amount of LSCache content that must be located and invalidated by URL.
* Added durable semantic-tag invalidation to the existing persistent `litespeed_invalidate` queue. Semantic targets are stored as bounded synthetic `tag:` targets in the same UltraCache-owned queue table, reuse the existing unique row hash, lease, retry, crash-recovery, and rerun semantics, and introduce no transient and no second queue table. Hard invalidation continues to dominate stale invalidation while pending/processing work is being coalesced.
* Fixed a durable invalidation coalescing edge case discovered while adding tag targets: a newly queued operation no longer inherits an old completed/error row's previous hard/stale/refill mode. Coalescing strength is retained only while the existing row is pending or processing; a new lifecycle after a terminal row starts from the newly requested mode. This applies to both exact URL and semantic-tag targets.
* Added signed native LiteSpeed `tags` and `stale-tags` control operations to the existing internal REST transport. Tag payloads are normalized, bounded, HMAC-signed, replay-claimed through the existing persistent lock infrastructure, and dispatched through the WordPress HTTP API path used by exact URL invalidation.
* Aligned the signed purge response with the native LiteSpeed Cache for WordPress purge syntax: public semantic/exact cache tags are emitted as `X-LiteSpeed-Purge: public[,stale],TAG1,TAG2` rather than relying on the older `tag=TAG` form. The existing exact URL tag itself is unchanged, so the behavior-test contract can continue validating `purge -> MISS -> HIT` against the same per-URL identity.
* Added per-page LiteSpeed semantic metadata sidecars (`.lstags`) beside canonical UltraCache HTML objects. Sidecars contain only bounded validated tag names, are written atomically through the existing guarded page-cache storage path, never contain request/user values, and are removed together with the matching HTML cache family.
* Made semantic tags survive the real warm/refill delivery path. The early `advanced-cache.php` drop-in reads the page's `.lstags` metadata and emits it together with the stable site tag and exact URL tag in `X-LiteSpeed-Tag`, so an LSCache object populated from an UltraCache HTML HIT receives the same semantic identity as a fresh WordPress render.
* Disabled direct Apache Static HTML Delivery only while native LiteSpeed HTML Cache owns the outer page-cache layer. A direct static alias cannot read per-page semantic metadata, so LiteSpeed MISS requests now pass through the very early UltraCache `advanced-cache.php` HIT path before LSCache stores them. The Apache rule synchronizer treats this as an intentional disabled state and removes an existing managed static block instead of reporting rule-generation failure. LiteSpeed HITs still bypass PHP entirely, while Varnish/non-LiteSpeed Apache static delivery remains unchanged.
* Added a persistent one-time semantic server-rule contract marker, `ultracache_litespeed_semantic_rules_contract`. On the first administrator request after upgrade, active LiteSpeed installations automatically resynchronize the managed LiteSpeed and Apache Static HTML `.htaccess` blocks so old direct-static rules cannot silently bypass semantic metadata. This migration uses normal WordPress option APIs and no transient.
* Added semantic-tag production metrics and history: semantic purge operations/successes/failures, stale semantic purge results, total semantic tags invalidated, and `tag-purge` history entries are retained in the existing persistent LiteSpeed metrics option.
* Extended LiteSpeed diagnostics with semantic-tag state, page-metadata delivery path, exact URL fallback status, server-rule contract state, and separate pending URL/tag target counts from the durable invalidation queue.
* Preserved the 3.01.08 performance model: content-save requests enqueue invalidation rather than waiting on signed purge loopbacks, semantic invalidation has no UltraCache wall-clock execution deadline, and later HTML/CSS/LiteSpeed refill continues through the shared persistent warm pipeline.
* Files changed:
  - `ultracache.php`
  - `README.md`
  - `readme.txt`
  - `changelog.txt`
  - `includes/bootstrap/class-bootstrap-trait.php`
  - `includes/core/html-variant-functions.php`
  - `includes/engine/class-engine-litespeed-response-trait.php`
  - `includes/engine/class-engine-storage-trait.php`
  - `includes/integrations/litespeed/class-litespeed-control-trait.php`
  - `includes/integrations/litespeed/class-litespeed-diagnostics-trait.php`
  - `includes/integrations/litespeed/class-litespeed-metrics-trait.php`
  - `includes/integrations/litespeed/class-litespeed-queue-trait.php`
  - `includes/lifecycle/class-plugin-data-cleanup-trait.php`
  - `includes/rest/class-rest-litespeed-trait.php`
  - `includes/rest/class-rest-routes-trait.php`
  - `includes/server/class-server-rules-trait.php`
  - `includes/settings/class-settings-migration-trait.php`
  - `includes/warmup/class-affected-url-coalescing-trait.php`
  - `includes/warmup/class-warm-runner-trait.php`
  - `templates/advanced-cache-template.php`

= 3.01.08 =
* Added durable targeted LiteSpeed invalidation using the existing persistent `wp_ultracache_cron_warm_queue` infrastructure with a dedicated `litespeed_invalidate` job type. No transient and no second queue table were introduced; the same permanent queue lease/recovery foundation already used by UltraCache automation now also owns LiteSpeed exact/stale invalidation lifecycle.
* Changed the normal `ultracache_after_purge_urls` LiteSpeed path from synchronous signed REST purge loopbacks to persistent enqueue. WordPress content-save/update requests now normalize and persist LiteSpeed invalidation intent, schedule the existing background worker, and return without waiting for one or more LiteSpeed control HTTP requests. Synchronous purge remains only as an exceptional correctness fallback for URLs that could not be persisted.
* Added atomic per-URL deduplication/coalescing. Repeated invalidations share one `litespeed_invalidate` row; hard invalidation dominates stale invalidation, refill intent is sticky, and a newer invalidation arriving while the URL is already processing sets the row for a second pass instead of being lost behind the active claim.
* Added durable LiteSpeed queue retry/recovery behavior without an UltraCache wall-clock execution deadline. Retryable signed-control failures are returned to pending state with bounded retry scheduling, terminal failures remain inspectable, abandoned processing claims are recovered by the existing queue lease recovery path, and the worker continues to use PHP `max_execution_time` for the actual HTTP execution contract.
* Made LiteSpeed invalidation execution independent from **Background warm pages per minute**. A value of 0 can pause later HTML/CSS refill work, but it no longer prevents pending LiteSpeed invalidation from draining. This keeps cache correctness independent from the administrator's page-warm throughput preference.
* Added bounded batch dispatch for queued LiteSpeed invalidation. Ready rows are grouped by hard/stale operation and sent through the existing signed internal control transport in native LiteSpeed URL batches; no raw replacement HTTP client was added and all transport continues through the WordPress HTTP API path.
* Changed targeted LiteSpeed refill ordering to `durable invalidation -> shared page warm -> public LiteSpeed refill`. Successful invalidation rows that requested refill enqueue the existing canonical page-warm pipeline. The resulting `litespeed-queued-invalidation` context records that purge has already completed, so the later LiteSpeed refill stage does not issue a redundant second exact purge.
* Preserved targeted refill semantics during coalescing. `lcp-refresh` invalidations can remain invalidation-only, while any coalesced normal affected-page invalidation upgrades the same URL row to retain refill intent.
* Preserved persistent LiteSpeed invalidation rows across ordinary warm-queue cleanup/manual-priority transitions. Flush All remains the explicit cache-generation boundary and intentionally clears obsolete queued URL invalidations before the fresh full-site external-cache purge/warm lifecycle begins.
* Added read-only LiteSpeed diagnostics for the durable invalidation queue: pending, processing, retrying, completed, terminal-error, and next-retry state are now exposed alongside existing behavior/metrics diagnostics.
* Added bounded retention cleanup for terminal LiteSpeed queue rows so successful history does not accumulate indefinitely while recent terminal failures remain available for regression diagnosis.
* Files changed:
  - `ultracache.php`
  - `README.md`
  - `readme.txt`
  - `changelog.txt`
  - `includes/integrations/litespeed/class-litespeed-control-trait.php`
  - `includes/integrations/litespeed/class-litespeed-diagnostics-trait.php`
  - `includes/integrations/litespeed/class-litespeed-queue-trait.php`
  - `includes/integrations/litespeed/class-litespeed-refill-trait.php`
  - `includes/warmup/class-cron-warm-orchestrator-trait.php`
  - `includes/warmup/class-targeted-warm-pipeline-trait.php`

= 3.01.07 =
* Unified the page-warm execution contract around PHP `max_execution_time`. The persistent cron warm worker no longer applies its previous independent 45-second operation hard cap, and queued per-page warm jobs no longer inject the previous fixed 20-second `time_budget`. Finite PHP execution limits remain the execution budget; `max_execution_time=0` means no UltraCache time deadline.
* Removed the fixed 20-second budget from Varnish authenticated origin refresh. Origin regeneration now uses the same warm engine execution contract as manual, cron, CLI, and LiteSpeed page warming instead of silently becoming shorter when Varnish two-stage refill is selected.
* Changed Varnish public refill requests from a fixed 10-second WordPress HTTP timeout to PHP `max_execution_time`, and changed Varnish refresh-ahead public probes from a fixed 8-second timeout to the same contract. These same-site requests continue through the WordPress HTTP API; the existing scoped `http_api_curl` bridge handles `max_execution_time=0` without the Requests one-second clamp and aligns connection timeout behavior for execution-bound work.
* Extended the shared guarded HTTP layer so `varnish_refill` and `varnish_refresh_probe` contexts are no longer clipped by the generic 60-second remote-request ceiling. This changes only execution-bound same-site warm/refill work; discovery, capability, admin-endpoint, and unrelated network diagnostics keep their own transport contracts for now.
* Added exact external-cache variant reporting to the unified warm pipeline. Varnish already returned bounded per-bucket refill details; LiteSpeed pipeline stages now retain the same bounded detail list so dashboard output can identify `orig`, `webp`, and `avif` individually instead of reporting only a numeric variant count.
* Updated warm UI summaries to show per-variant outcomes such as `LiteSpeed orig ✓ / webp ✓` and to expose mixed failures such as `LiteSpeed orig ✓ / webp ✗`. The same formatter is used for Varnish variant results. Inconclusive successful LiteSpeed observations are displayed with `?` rather than being indistinguishable from verified cache evidence.
* Promoted successful-but-inconclusive LiteSpeed refill evidence to a real pipeline `warning` stage. Warning stages still satisfy the warm dependency contract, but the overall pipeline is now reported as `completed_with_warnings` and retains `hasWarnings=true` instead of flattening the result to an ordinary completed stage.
* Preserved queue/URL-lock lease TTLs and heartbeat renewal intervals. These are coordination/recovery lifetimes rather than execution deadlines and are still renewed while work is active; this release removes only arbitrary warm execution/request caps.
* Files changed:
  - `ultracache.php`
  - `README.md`
  - `readme.txt`
  - `changelog.txt`
  - `includes/core/debug.php`
  - `includes/core/http-guards.php`
  - `includes/warmup/class-cron-warm-orchestrator-trait.php`
  - `includes/warmup/class-warm-page-pipeline-trait.php`
  - `includes/integrations/varnish/class-varnish-origin-refill-trait.php`
  - `includes/integrations/varnish/class-varnish-refill-trait.php`
  - `includes/integrations/varnish/class-varnish-refresh-ahead-trait.php`
  - `includes/admin/js/warmup.js`

= 3.01.06 =
* Introduced Cacheability Policy v2 for public HTML storage. When **Cache Public Pages with Response Cookies** is enabled, a response `Set-Cookie` header no longer rejects the page merely because a cookie was created. Incidental PHP session cookies such as `PHPSESSID` are allowed to coexist with public HTML storage when no private/auth/cart response-cookie signal is present. UltraCache continues to store only the HTML body; response `Set-Cookie` headers are never written into or replayed from the UltraCache page-cache file.
* Preserved strict response-cookie mode for administrators who explicitly disable **Cache Public Pages with Response Cookies**. In strict mode, any response `Set-Cookie` still rejects HTML storage exactly as before. The canonical default for new/missing settings is now enabled to favor cache coverage and speed; an explicitly saved disabled value remains disabled.
* Added a separate hard response-cookie reject policy for authentication/protected-content and active WooCommerce customer/cart state (`wordpress_logged_in_`, `wordpress_sec_`, `wp-postpass_`, `woocommerce_items_in_cart`, `woocommerce_cart_hash`, and `wp_woocommerce_session_`). These response cookies still reject public HTML storage even while Policy v2 is enabled. `PHPSESSID` is deliberately not in this default list because session creation alone does not prove that the rendered HTML varies by session.
* Added the WordPress filter `ultracache_response_cookie_reject_patterns` so integrations can extend the response-cookie private-state list without patching UltraCache core.
* Added the WordPress filter `ultracache_cache_bypass_cookie_patterns` for incoming request cookies. The same filter is applied both by the PHP page-cache decision layer and by generated native LiteSpeed cookie-bypass rules so extension behavior does not diverge between UltraCache HTML retrieval and LSCache retrieval.
* Kept the persistent **Never Cache When These Cookies Exist** setting as the primary editable incoming-state safeguard. Logged-in, protected-content, WooCommerce cart/session, wishlist/compare, and other configured cookies continue to bypass public HTML cache before storage/serve decisions. No Booked-specific exception or plugin-file modification was added.
* Extended internal warm/debug response diagnostics with `X-Ultra-Cache-Set-Cookie-Names` plus `X-Ultra-Cache-Response-Cookie-Policy`. Warm results and HTML pipeline details now retain observed response cookie names/policies, allowing a public-cache decision to be traced without storing cookie values.
* Fixed queued dashboard warm completion text incorrectly reporting `Cached:` for terminal skipped/failed results. Queued warm results now report `Skipped:` with the real backend reason or `Failed:` for unsuccessful jobs; successful jobs alone report `Cached:`.
* Updated Cache settings/help/diagnostics copy to describe Policy v2 accurately: response-cookie creation is not equivalent to personalized HTML, while incoming sensitive state and explicit private/no-store/DONOTCACHEPAGE safeguards remain authoritative.
* Files changed:
  - `ultracache.php`
  - `README.md`
  - `readme.txt`
  - `changelog.txt`
  - `includes/class-ultra-cache-engine.php`
  - `includes/engine/class-engine-cache-decision-trait.php`
  - `includes/warmup/class-warm-runner-trait.php`
  - `includes/warmup/class-warm-page-pipeline-trait.php`
  - `includes/server/class-server-rules-trait.php`
  - `includes/settings/class-settings-registration-trait.php`
  - `includes/settings/class-settings-validation-trait.php`
  - `includes/admin/js/warmup.js`
  - `includes/admin/js/dashboard-application.js`
  - `includes/admin/js/dashboard-diagnostics-ui.js`
  - `includes/admin/js/help.js`

= 3.01.05 =
* Fixed PHP `max_execution_time=0` being converted into an effective one-second total-request timeout by the WordPress Requests cURL transport. UltraCache keeps using the WordPress HTTP API and temporarily hooks the official `http_api_curl` extension point only around scoped execution-bound requests. For finite PHP execution budgets, the cURL connect timeout is aligned to the same request budget instead of retaining Requests' independent 10-second default; for `max_execution_time=0`, UltraCache removes its total-request deadline and the Requests one-second clamp. The hook is removed immediately after the request, and no raw replacement HTTP client or invented large timeout is introduced.
* Setup Wizard front-page font detection no longer uses a fixed 10-second HTTP timeout. The same-site scan now inherits PHP `max_execution_time`, including true unlimited behavior when it is zero.
* Setup Wizard live HTML compression detection no longer uses a fixed 5-second HTTP timeout for Brotli/gzip probes. Both probes now inherit PHP `max_execution_time`, including true unlimited behavior when it is zero.
* Removed the Setup Wizard/settings synchronization 15-second Promise race. Wizard actions now wait for the actual queued settings save to settle instead of failing because an arbitrary client-side deadline elapsed.
* Removed the dashboard action poller's 360-second client-side safety deadline (`480 × 750 ms`). Resumable dashboard work now remains attached to its persistent job until the server reports a terminal `done` or `failed` state.
* Removed the Browser Runtime Scan's fixed 300-second hard timeout used by the Setup Wizard JavaScript verification step. The browser scanner now completes from its explicit collector completion event, or fails from an iframe load error, instead of manufacturing a separate five-minute UltraCache execution deadline.
* Setup Wizard server analysis now reuses the persistent LiteSpeed detection evidence introduced in 3.01.04 when the current request cannot identify LiteSpeed directly. The Setup Plan remains read-only: it reads the persistent WordPress option through the LiteSpeed subsystem and does not trigger a new probe merely to build the Wizard plan.
* Preserved the existing WordPress HTTP APIs (`wp_safe_remote_request()` / guarded UltraCache wrappers) for all changed Wizard and LiteSpeed requests; no raw replacement HTTP client was introduced.
* Files changed:
  - `ultracache.php`
  - `README.md`
  - `readme.txt`
  - `changelog.txt`
  - `includes/core/http-guards.php`
  - `includes/rest/class-rest-routes-trait.php`
  - `includes/runtime/class-runtime-cache-services-trait.php`
  - `includes/integrations/litespeed/class-litespeed-transport-trait.php`
  - `includes/setup/class-setup-planning-trait.php`
  - `includes/admin/js/dashboard-settings-actions.js`
  - `includes/admin/js/dashboard-application.js`

= 3.01.04 =
* Fixed LiteSpeed warm-up and targeted refill being blocked in WP-CLI/cron/background contexts when `$_SERVER['SERVER_SOFTWARE']` did not expose the frontend web server. LiteSpeed purge/refill operations now reuse confirmed persistent origin/cache evidence instead of treating the current PHP execution context as the only source of truth.
* Added persistent LiteSpeed detection state in the non-autoloaded WordPress option `ultracache_litespeed_detection_v1`; no LiteSpeed detection result is stored in a transient. The record keeps an origin fingerprint, confirmed server/cache signals, evidence source, confirmation time, and the last explicit same-site origin probe result.
* Added an on-demand same-site LiteSpeed origin probe for operations/diagnostics that require confirmed origin evidence and have neither current-request nor persistent evidence. The probe uses the WordPress HTTP API through UltraCache's trusted same-site loopback guard and records the resulting `Server`, `X-LiteSpeed-Cache`, and `X-QC-Cache` evidence persistently.
* Changed LiteSpeed signed-control and public-refill request timeouts from the previous fixed 10-second value to PHP `max_execution_time`; `max_execution_time=0` remains unlimited. The shared guarded HTTP layer now preserves the caller-supplied max-execution timeout for LiteSpeed contexts instead of clipping it to 60 seconds.
* Extended LiteSpeed refill/behavior diagnostics with `Cache-Control`, `X-LiteSpeed-Cache-Control`, and `X-Ultra-Cache-Reason` so a future BYPASS/SKIP can be traced to the response that caused it instead of appearing as a generic inconclusive HTTP 200.
* LiteSpeed refill responses that return HTTP 200 without an observable HIT/MISS signal are still treated as completed requests, but are now explicitly marked as warning/inconclusive evidence instead of being indistinguishable from a verified LiteSpeed cache response.
* Added cleanup/uninstall coverage for the new persistent LiteSpeed detection option.
* Bumped the persistent external-cache detection snapshot schema so existing 3.01.03 snapshots are rebuilt once and cannot keep reporting the previous CLI-context LiteSpeed false negative after upgrade.
* Files changed:
  - `ultracache.php`
  - `README.md`
  - `readme.txt`
  - `changelog.txt`
  - `includes/core/http-guards.php`
  - `includes/integrations/litespeed/class-litespeed-transport-trait.php`
  - `includes/integrations/litespeed/class-litespeed-control-trait.php`
  - `includes/integrations/litespeed/class-litespeed-refill-trait.php`
  - `includes/integrations/litespeed/class-litespeed-diagnostics-trait.php`
  - `includes/runtime/class-runtime-cache-services-trait.php`
  - `includes/lifecycle/class-plugin-data-cleanup-trait.php`
  - `uninstall.php`

= 3.01.03 =
* Extended **Protect WPBakery animations** to keep the audited lifecycle-critical `wpb_flexslider`, `nivo-slider`, `isotope`, `jquery-ui-tabs`, and `jquery-ui-accordion` providers out of UltraCache Delay queues alongside `vc_waypoints` and `vc_carousel_js`. These scripts remain eligible for normal defer optimization.

= 3.01.02 =
* Extended **Protect WPBakery animations** to keep `vc_carousel_js` initialization out of UltraCache Delay queues as well as `vc_waypoints`, preventing WPBakery image carousels from remaining hidden when their `window.load` initialization would otherwise be delayed. Both scripts remain eligible for normal defer optimization.

= 3.01.01 =
* Added **Protect WPBakery animations** under Advanced Frontend Javascript manipulation. When enabled, WPBakery/Visual Composer `vc_waypoints` animation-reveal JavaScript is kept out of UltraCache Delay queues while remaining eligible for normal defer optimization.
* Setup Wizard now detects active WPBakery/Visual Composer installations and automatically enables **Protect WPBakery animations** without adding entries to JavaScript safeguard lists or changing other `vc_*` scripts.

= 3.00.16 =
* Cleared Browser Runtime Scan WordPress coding-standard warnings without changing scanner behavior: scanner query reads now document their short-lived token verification, and the current request URI is sanitized before scan-target normalization.

= 3.00.15 =
* Browser Runtime Scan anonymous passes now run inside a credentialless iframe authorized by a short-lived, same-site scan-only token, so WordPress resolves the request as anonymous from the beginning without `wp_set_current_user()` or authentication-cookie manipulation.
* Removed the administrator REST nonce from scanner frontend documents; the scan token grants no WordPress user, capability, settings access, or write action and only enables the collector for its bound scan id and URL.
* `Debug Browser Scanner` now exposes the same credentialless scanner iframe visibly inside the admin page instead of opening a credential-bearing popup; unsupported browsers fail explicitly instead of producing a differential from the wrong context.
* Removed the obsolete logged-in/admin Runtime Scan context because logged-in frontend optimization is intentionally bypassed.
* Runtime Scan baseline state now preserves unrelated font delivery, iframe lazy-loading, WooCommerce Blocks CSS cleanup, critical-request-chain relief, and cart-fragment suppression settings instead of changing them during a JavaScript differential scan.

= 3.00.14 =
* Moved the existing Browser Scanner visible-window debug setting into `Advanced Frontend Javascript manipulation` and renamed its UI label to `Debug Browser Scanner` without changing the stored setting key or current saved state.
* Runtime Scan Comparison diagnostics now activate and render only while `Debug Browser Scanner` is enabled; disabling the setting clears any temporary comparison state while normal hidden-iframe Runtime Scan behavior remains unchanged.

= 3.00.13 =
* Logged-in frontend requests now bypass UltraCache frontend performance transformations, including JavaScript defer/delay/reordering, CSS bundle/delay helpers, font delivery rewrites, asynchronous CSS/iframe helpers, asset-chain cleanup, full-HTML performance rewrites, and frontend media URL replacement.
* Page-cache bypass behavior for logged-in users is unchanged; upload/background media conversion, object cache, invalidation, diagnostics, and administrative/background workflows remain available.
* Runtime Scanner `Anonymous frontend` requests continue to switch to the anonymous user before frontend transformations execute, while real logged-in browsing remains unmodified.

= 3.00.12 =
* When `Open Browser scanner in new window` is enabled, Runtime Scan now pauses immediately after the unoptimized baseline capture and shows a manual `Continue` button before restoring JavaScript optimizations or starting cycle 1.
* The visible Browser Scanner window remains on the captured baseline page during the pause so its live page and DevTools console can be inspected; the existing hidden iframe path remains unchanged.

= 3.00.11 =
* Added the Advanced setting `Open Browser scanner in new window` with a canonical default of Off; existing hidden-iframe Browser Runtime Scan behavior remains unchanged while the setting is Off.
* When the visible Browser Scanner setting is enabled, Runtime Scan pre-opens a real browser window from the user click, reuses it for baseline and optimized passes, and keeps it open for live page/DevTools inspection.
* Runtime Scan collector bridge delivery now supports both the existing iframe parent and the optional visible window opener without adding a second scanner or REST polling path.

= 3.00.10 =
* Fixed Runtime Scan canonical Warm session acquisition to use the existing `warm` foreground job type instead of the unsupported `runtime_scan` job type that returned HTTP 409 before warming.

= 3.00.09 =
* Runtime Scan now warms its selected URL through the existing manual Warm page pipeline and foreground warm session instead of bypassing the canonical pipeline with a direct low-level warm_url call.
* Page warm execution budgets and loopback HTTP request timeouts now follow PHP max_execution_time instead of arbitrary 10/20/45/60/75-second page-warm caps; max_execution_time=0 is treated as unlimited for the warm request.
* CSS bundle source requests invoked by the Warm pipeline now inherit the same max_execution_time-based request timeout instead of reintroducing a 10/120-second cap.
* Safe page-warm and CSS-bundle loopback requests no longer truncate an explicitly requested timeout to 60 seconds, allowing those warm callers to honor the server execution limit.
* Warm URL locks and foreground/cron ownership leases are no longer allowed to expire before a finite PHP max_execution_time window can complete.

= 3.00.08 =
* Browser Runtime Scan no longer self-finalizes after 12 seconds or on beforeunload; a normal capture now completes only after the scanned page fires window load and the existing 4.5-second settle period, while the dashboard's 300-second hard timeout remains the only emergency cutoff.
* The unoptimized Runtime Scan pass now also disables and later restores the full WooCommerce frontend strategy state, including product/gallery asset cleanup, product-filter asset cleanup, WooCommerce Blocks CSS cleanup, and Cart fragments behavior.

= 3.00.07 =
* Browser Runtime Scan now waits up to 300 seconds for the frontend collector to complete instead of aborting after 20 seconds.
* A Runtime Scan hard-timeout is reported explicitly as a timeout failure and is never recorded or presented as a clean zero-error scan.

= 3.00.06 =
* Fixed Runtime Scan comparison so both captures open the exact same scan URL and use the same frontend rendering path.
* Removed the baseline-only runtime settings override and changing baseline/optimized URL markers.
* Runtime Scan still disables/restores the normal JavaScript optimization switches between captures while preserving safeguard lists unchanged.

= 3.00.05 =
* Keep Defer Instead of Delay and Do Not Defer or Delay completely untouched during Runtime Scan baseline capture; the baseline now snapshots and temporarily disables only JavaScript optimization engine settings before Flush/Warm/capture, then restores only those settings before the optimized pass.
* Prevent Runtime Scan baseline preparation from overwriting default, manually maintained, or previously learned JavaScript safeguards while preserving the existing differential scan and Error Fixer flow.

= 3.00.04 =
* Make Runtime Scan baseline use the real saved JavaScript configuration lifecycle: snapshot current JavaScript optimization settings and the Defer Instead / Do Not Defer or Delay lists, temporarily disable the JavaScript optimization engines and clear both safeguard lists, Flush All, warm the selected scan URL, capture the baseline, then restore the exact snapshot before the optimized Flush/Warm/browser scan.
* Guarantee restoration of the original JavaScript settings and safeguard lists through a finally path if baseline preparation, warm-up, or browser capture fails.
* Keep the temporary Runtime Scan Comparison diagnostics from 3.00.03 so baseline, raw optimized, and differential sets can be verified while this flow is tested.

= 3.00.03 =
* Added temporary Runtime Scan comparison diagnostics for baseline, raw optimized, and differential errors with normalized matching signatures; scan and repair behavior are unchanged.

= 3.00.02 =
* Make Browser Runtime Scan differential: capture a baseline request with UltraCache JavaScript manipulation disabled, flush and run the optimized scan, and send only new optimized-only runtime errors to JavaScript Error Fixer while retaining baseline evidence in the scan result.
* Keep the baseline request isolated to the diagnostic URL instead of globally changing live JavaScript settings, and treat the new Runtime Scan mode query argument as an internal non-cacheable control parameter.
* Let the Setup Wizard resolve ambiguous Defer Instead / Do Not Defer or Delay findings by trying Defer Instead first, then escalating the same persistent candidate to Do Not Defer or Delay on the following scan through the existing safeguard state.
* Complete the Setup Wizard with a browser-check notice instead of a failed setup state when optimized-only runtime errors remain after all confident automatic safeguards have been exhausted.

= 3.00.01 =
* Rename the WordPress-facing plugin title to UltraCache - Cache and Speed Optimization while preserving the ultracache slug, folder, text domain, function prefixes, and internal identifiers.
* Promote UltraCache to the 3.00.01 release series without changing runtime behavior beyond the version metadata.

= 2.59.13.84 =
* Invalidate the PHP OPcache entry for wp-config.php immediately after a verified UltraCache-managed write so newly saved Varnish/Redis secret constants are visible to the next request without waiting for OPcache timestamp revalidation.
* Surface the runtime planner reason when Varnish exact-invalidation capability detection is blocked before any transport request is sent.

= 2.59.13.83 =
* Make administrator-triggered Varnish exact-BAN capability probes retry up to three times only for transient Admin transport failures while keeping authentication, invalid-command, and explicit Varnish rejection failures immediate.
* Accept Varnish CLI 200 and 201 success responses for Admin BAN operations and surface the last concrete transport result directly in the canary failure message when capability detection still fails.

= 2.59.13.82 =
* Restore the compatibility export for the Varnish default-endpoint helper after the mode-aware endpoint synchronization change, preventing varnish.js from failing module registration and cascading into cache/dashboard bootstrap failures.

= 2.59.13.81 =
* Keep the Varnish endpoint field synchronized when Purge mode changes: replace the previous mode's populated default endpoint with the new mode's default, including the equivalent http://127.0.0.1:82 form, while preserving custom endpoints.

= 2.59.13.80 =
* Give Redetect Varnish Capabilities a dedicated 20-second Varnish admin-socket timeout for diagnostic exact-invalidation probes, while preserving the user's normal production invalidation timeout.

= 2.59.13.79 =
* Make Save Varnish Settings run the existing Varnish capability detection after a successful save and persist the Varnish settings again after successful capability detection, reusing shared save/test helpers without nested UI-queue operations.
* Rename the dashboard Test Varnish action to Redetect Varnish Capabilities and remove the standalone Measure Varnish Performance button while retaining the existing performance measurement subsystem internally.

= 2.59.13.78 =
* Extend the unified Setup Wizard configuration progress with Query-string args whitelist population before font optimization, reusing the existing Populate detector and normal settings save flow so detected taxonomy/attribute keys are appended without replacing user-maintained entries.

= 2.59.13.77 =
* Move Scan Browser Runtime Errors cycle progress from the inline JS / Defer Safeguards & Diagnostics panel into a dedicated live modal while reusing the same Runtime Scan status, repair-cycle runner, and stored results.

= 2.59.13.76 =
* Enable LCP Frontend Discovery and Lazy load third-party iframes in the existing recommended Optimal Settings recipe used by the unified Setup Wizard.

= 2.59.13.75 =
* Hide the Setup choices configuration section immediately after the Wizard Next action starts configuration, keeping the selected values in the existing shared setup flow while progress takes over the modal.

= 2.59.13.74 =
* Rework the unified Setup Wizard review layout so Setup choices appears directly below the explanatory text and spans nearly the full modal width, without changing Wizard behavior or underlying settings flows.

= 2.59.13.73 =
* Simplify the unified Setup Wizard UI by removing the visible Website analysis summary while retaining the same internal detection for recommended defaults and capability decisions.
* Make Homepage the default Wizard warm-up scope and use Homepage as the fallback when no valid Wizard warm-up selection is available.
* Reduce the Wizard setup footer to a single Next action; closing or postponing remains available through the modal close control.

= 2.59.13.72 =
* Merge the fresh-install Setup Wizard and the Overview optimal-configuration action into one shared Setup Wizard path; Overview now exposes a single Run Wizard action and removes the duplicate Flush All Cache / Warm Site buttons from the Optimal Configuration card.
* Add Wizard Object Cache and warm-up selectors without creating parallel settings or workers: Object Cache reuses the existing backend probe/save/runtime-verification path, while Homepage, Homepage + Menu, and Full Site reuse the normal Warm Cache menu/depth/source settings and existing warm runners.
* Update Optimal Configuration guidance so manually maintained credentials, schedules, exclusions, safeguards, and lists are explicitly preserved, and allow the same setup-wizard state machine to be started manually on existing installations.

= 2.59.13.71 =
* Forward the active dashboard manual-session owner token from homepage-first media processing into the shared attachment worker, preventing a false HTTP 409 exclusive-queue conflict during live homepage conversion while leaving local-asset processing and queue ownership semantics unchanged.

= 2.59.13.70 =
* Use WordPress core `wp_is_writable()` directly when setup planning probes the object-cache disk backend, removing the direct PHP `is_writable()` fallback flagged by WordPress Coding Standards without changing the backend availability decision.

= 2.59.13.69 =
* Make Start / Resume Conversion refresh homepage media discovery under the existing exclusive dashboard media session, convert homepage attachments and local theme/plugin image assets first in the foreground, then cross a semantic homepage checkpoint and continue the normal full Media Library conversion.
* Make Apply Optimal Settings reuse the same homepage foreground conversion phase but stop at the homepage checkpoint, release manual media ownership without resuming background media work, and provide a Skip image conversion action that exits after the current conversion unit without marking remaining homepage media failed.

= 2.59.13.68 =
* Make Apply Optimal Settings and the First-Run Setup Wizard reuse the standalone Runtime Scan self-healing cycle: the same target-only Purge/Warm preparation, iframe Browser Runtime Scan, Console Error Fixer request, deterministic safeguard save, zero-error stop, and six-scan limit now run from one shared implementation.
* Remove the obsolete Apply-only hidden runtime scanner/polling and JavaScript safeguard repair helpers so automatic setup cannot diverge from the proven standalone Runtime Scan flow.

= 2.59.13.67 =
* Make every standalone Runtime Scan self-healing cycle honor Page URL to scan as the single warm and scan target: after Purge All, warm only that exact URL instead of warming the homepage, configured menu, or starting the background full-site warm-up.
* Resolve the Runtime Scan target URL once at cycle start and reuse the same value for warm-up, iframe capture, and JavaScript Error Fixer analysis so the cycle cannot drift to another configured warm scope.

= 2.59.13.66 =
* Turn the standalone Scan Browser Runtime Errors action into a bounded self-healing cycle: before each scan it clears cache and warms the configured site scope, captures errors through the existing iframe runtime scanner, sends those exact errors through the existing Console Error Handler, saves only deterministic recommended Defer Instead / Do Not Defer or Delay actions, and repeats until zero runtime errors or six scans.
* Stop the Runtime Scan cycle when Error Fixer cannot prove a new deterministic action; the sixth scan is final verification and remaining errors stay visible instead of applying an unverified final change.
* Remove Apply Optimal Settings' automatic Defer JS dependency preset append so JavaScript safeguards are no longer pre-seeded outside Error Fixer evidence.

= 2.59.13.65 =
* Replace the manual Scan Browser Runtime Errors popup/REST-polling transport with an transparent fixed 1280×900 iframe that returns one completed serialized runtime payload directly to the admin through a scan-ID- and origin-validated `postMessage` bridge; the manual scan no longer depends on runtime report REST storage or polling.
* Keep Browser Runtime Scan as input to the existing Console Error Handler: captured errors continue through the same `runConsoleErrorFixer()` / `scanType: console` path used by Extract Console Error Suggestions, with no alternate suggestion engine or scan fallback.

= 2.59.13.64 =
* Make Scan Browser Runtime Errors persist one completed raw browser-error report instead of posting on every captured error, removing report-write contention from the collector path.
* Remove JavaScript Error Fixer/suggestion generation from runtime report save/read operations; runtime reporting now stores and returns raw browser evidence only.
* Feed captured Runtime Scan errors into the same Console Error Handler path used by Extract Console Error Suggestions, including the same `scanType: console` diagnostic queue engine and result rendering.

= 2.59.13.63 =
* Make Apply Optimal Settings JavaScript verification proof-required: remove the HTML dependency analyzer fallback and the hidden snapshot-report fallback from the automatic compatibility path.
* Fail Apply Optimal Settings when any tested runtime page does not return a complete persisted report, when Error Fixer cannot prove an automatic action for remaining runtime errors, or when errors remain after five verification passes; do not mark setup complete without verified JavaScript compatibility.

= 2.59.13.62 =
* Enable **Delay non-critical/local JS** in Apply Optimal Settings and move its dashboard control from Advanced Frontend Javascript manipulation into the main Javascript manipulation card immediately above Delay safe third-party JS.
* Make the existing Apply Optimal Settings / First-Run JavaScript compatibility loop consume exact recommended Error Fixer actions from its hidden runtime scans before using HTML dependency analysis as a fallback; automatically apply only resolved Defer Instead or Do Not Defer or Delay targets, purge/rewarm, and verify for up to five passes with early stop when clean or when no new deterministic fix exists.

= 2.59.13.61 =
* Expand a proven missing-global dynamic function-dispatch failure across the same literal runtime dispatch collection in one Error Fixer pass: only function names present in that exact runtime set, with exactly one loaded provider and a proven provider/dispatcher strategy conflict, become fixes; no same-owner, filename, plugin, or symbol-prefix expansion is used.

= 2.59.13.60 =
* Make jQuery-plugin Error Fixer ordering use the exact loaded provider and direct-consumer page-inventory records already proven by Runtime Scan, instead of re-resolving their filenames/handles through fuzzy inventory matching that could manufacture multi-strategy ambiguity.

= 2.59.13.59 =
* Keep explicit `jQuery(...).method is not a function` failures inside the jQuery-plugin dependency resolver: accept a direct consumer only when scanned page code actually calls the missing method, so wrapper/runtime stack frames cannot displace the real consumer.
* When the loaded jQuery plugin provider/consumer pair or its execution-order proof is incomplete, keep the finding review-only inside the jQuery-plugin resolver instead of falling through to the generic failing-consumer exclusion fallback.

= 2.59.13.58 =
* Separate wrong-type JavaScript TypeErrors from missing-global provider resolution: `X.foo is not a function` now proves that `X` exists, so Error Fixer repairs only the exact failing consumer strategy (`Delay` → `Defer Instead`, then non-delayed `defer`/`async` → `Do Not Defer or Delay`) instead of inventing a provider for `X`.
* Reject bare self-referential symbol transforms such as `x = JSON.parse(x)` or `x = normalize(x)` as provider-definition evidence during local source discovery, while retaining real declarations/definitions for genuine `ReferenceError: X is not defined` analysis.

= 2.59.13.57 =
* Make JavaScript Error Fixer action rendering total for appendable findings: a resolved `preferredTarget` still shows only its exact recommended action, while an appendable finding with no resolved target now exposes the two exact choices (`Defer Instead` and `Add to Do Not Defer or Delay`) instead of rendering with no button; dependency-chain actions remain hidden in this unresolved case.
* Treat same-owner symbol providers found only by filesystem/code discovery as review-only evidence when that provider is absent from the scanned page inventory, instead of presenting an unloaded file as an active appendable runtime provider.

= 2.59.13.56 =
* Make unresolved runtime-source fallback findings actionable in JavaScript Error Fixer: when no concrete provider can be resolved and the exact plugin/theme source is retained as the compatibility fallback, mark the finding for `Do Not Defer or Delay` so the recommended append button is shown instead of leaving a ready-to-append finding with no action.

= 2.59.13.55 =
* Make jQuery-plugin Error Fixer dependency findings strategy-aware: when one concrete provider and direct consumer have a proven runtime ordering conflict, recommend only the provider with the least-invasive `Defer Instead` or `Do Not Defer or Delay` target selected from their scanned strategies; keep the consumer as causal evidence instead of a duplicate appendable fix.
* Do not emit jQuery-plugin provider/consumer appendable findings when Runtime Scan cannot prove a single actionable execution-order conflict; continue through the existing error-scoped fallback pipeline instead.

= 2.59.13.54 =
* Restore anonymous inline third-party content-pattern delay with boundary-aware prefix matching: user-visible token-prefix fragments ending in `-` or `_` must begin at a real identifier boundary, preventing suffix collisions inside unrelated application identifiers while preserving normal URL/domain/code and established simple-token matching.
* Keep the WordPress registered dependency-closure protection for Do Not Defer or Delay unchanged.

= 2.59.13.53 =
* Restore the anonymous-inline JavaScript safeguard removed in 2.59.13.52: third-party content-pattern delay now requires a canonical WordPress-style inline companion with both a captured handle and a matching `-js` / `-js-before` / `-js-after` / `-js-extra` / `-js-translations` ID family, preventing arbitrary inline application bootstraps from being delayed by incidental pattern matches.
* Retain the 2.59.13.52 WordPress registered dependency-closure protection for Do Not Defer or Delay unchanged.

= 2.59.13.52 =
* Make the visible Do Not Defer or Delay list dependency-preserving for WordPress-registered scripts: when an explicitly excluded registered handle is kept blocking, its real registered dependency closure inherits the same runtime protection so UltraCache cannot create a deferred provider before a blocking consumer.
* Restore anonymous inline third-party content-pattern delay behavior removed in 2.59.09.08, allowing visible patterns such as gtag/dataLayer/GTM bootstrap markers to delay matching inline analytics again while retaining user exclusions and WordPress inline dependency-block protection.

= 2.59.13.51 =
* Make JavaScript Error Fixer action controls honor each finding's backend `preferredTarget`: confirmed console findings show only the exact recommended Defer Instead or Do Not Defer or Delay action, while Analyzer/general diagnostic action choices remain unchanged.
* Split Console Error Handler bulk actions by recommendation target, including correct escalation from an already-applied Defer Instead safeguard to Do Not Defer or Delay when the backend marks the finding as a fallback candidate.

= 2.59.13.50 =
* Canonicalize WordPress script families in JavaScript diagnostics using real `data-ultracache-handle` registry evidence: matching external, `-js-before`, `-js-after`, `-js-extra`, and `-js-translations` fragments inherit the same handle and declared dependency metadata, so Error Fixer can diagnose the parent dependency chain instead of falling back to the consumer.
* Do not infer new WordPress families from script IDs alone; sibling propagation occurs only when an explicit WordPress handle was captured for that family.

= 2.59.13.49 =
* Make JavaScript Error Fixer resolve WordPress inline `*-js-after` companions to their owning enqueued script before dependency diagnosis. If that parent is defer/delay/async, recommend the parent as the direct execution-order fix; if the parent is already blocking, inspect its own declared WordPress dependencies instead.
* When multiple late providers are all direct declared dependencies of the same exact failing consumer, report those broken edges instead of falling back to the consumer itself; unrelated page dependency conflicts remain excluded from Error Fixer results.

= 2.59.13.48 =
* Consolidate console errors within the same pasted batch: a later unresolved fallback is suppressed only when an earlier resolved failure is causally linked by the same script identity, the real WordPress dependency ancestry, or concrete provider code for the later missing symbol. Independent errors remain visible.
* Treat WordPress inline `*-js-before` / `*-js-after` frames as companions of their owning enqueued script, so the Error Fixer proposes one parent target instead of duplicate inline-and-parent exclusions.
* Keep error-scoped WordPress registry/provider findings in the Error Fixer Appendable Fixes bucket even when the minimal action is Do Not Defer or Delay; Analyzer-only page-wide findings remain separate.

= 2.59.13.47 =
* Restore JavaScript Error Fixer to an error-scoped pipeline: remove the page-wide declared-dependency and lifecycle Analyzer passes from console/runtime error fixing, so unrelated silent dependency findings no longer appear as fixes for a specific browser error.
* Resolve each failing script against its actual WordPress-declared dependencies first and recommend only a unique execution-order conflict; if the registry does not explain the failure, fall back to a unique loaded symbol provider and then targeted active plugin/theme discovery instead of broad suggestion fan-out.
* Remove hardcoded WordPress missing-symbol-to-provider mappings from the Error Fixer, stop treating arbitrary `object.method is not a function` errors as jQuery plugin failures, and never recommend multiple method consumers when no concrete provider was found.

= 2.59.13.46 =
* Simplify the Media queue summary to completed, pending, processing, and failed states. Completed combines successfully processed items with queue items that were already up to date, removing the misleading processed/already optimized split.

= 2.59.13.45 =
* Clarify the Media batch summary cards: show AVIF and WebP optimized-file counts on separate lines, rename the ambiguous Media status total to Media queue items, include processed queue items in the breakdown, and show Target policy, Fallback, and Queue format on separate lines.

= 2.59.13.44 =
* Make successful Varnish capability proofs persistent until their bound configuration/capability fingerprint changes or a new test supersedes them; remove the seven-day proof expiry from exact invalidation, topology/flush scope, ESI, HTML variants, batch BAN, and soft-purge-related capability state. Legacy stored expiry timestamps are ignored so previously verified capabilities remain valid after upgrade.
* Remove Varnish proof-expiry and per-endpoint tested-at timestamps from the diagnostics UI; Test Varnish still records internal probe timestamps for state/diagnostic purposes but no longer presents when a successful capability test was performed.

= 2.59.13.43 =
* Fixed the WordPress 6.7+ early translation-loading notice in the pre-init Varnish ESI capability status path by routing its four runtime messages through the existing init-aware translation helper.

= 2.59.13.42 =
* Fix Overview → Warm Site so it honors the persistent **Also warm CSS bundles** preference exactly like Cache → Warm Cache → Full Site, including the configured CSS bundle scope, CSS readiness guard, and matching resumable warm job type.

= 2.59.13.41 =
* Fix the WordPress 6.7+ early translation-loading notice for the query-combination-limit warm bypass message by routing it through the existing init-aware translation helper.

= 2.59.13.40 =
* Add **Query cache combination level** with 1 = 8, 2 = 64, 3 = 512 (default), 4 = 4,096, and ALL (NOT RECOMMENDED), plus a live bounded calculator based on the effective query-string allowlist; very large allowlists use scientific notation instead of allocating enormous integers or combination sets.
* Replace page-cache query-family rotation with hard admission control: canonical URLs do not consume query capacity, orig/WebP/AVIF representations share one query identity, existing cached variants remain usable, and new unknown variants bypass cache once the configured per-path limit is full instead of evicting older variants and creating unbounded registry churn.
* Add independent hourly and catch-up garbage collection for `wp_ultracache_cache_asset_refs`, deleting only expired inactive rows in bounded 5,000-row batches with a per-run delete/time budget; stale generated-CSS cache invalidation now uses the canonical cache deletion path so affected registry rows are marked inactive correctly.

= 2.59.13.39 =
* Fix Browser Runtime Scan report races that could leave real JavaScript errors unrecorded while repeated `/runtime-js-scan/report` requests returned HTTP 409. The frontend collector is now the normal serialized writer, admin scan loops poll persisted reports instead of POSTing competing snapshots, final collector flush is requested explicitly, and direct DOM snapshot submission is retained only as a fallback.
* Reduce runtime-scan report pressure by replacing the collector's 1-second overlapping report loop with one baseline snapshot, error-driven serialized updates, and bounded completed snapshots; add short bounded server-side merge-lock acquisition retries for residual cross-navigation contention.
* Enable **Async external CSS** in Apply Optimal Settings while preserving the administrator-maintained external CSS async exclusion list.

= 2.59.13.38 =
* Add automatic CSS Bundle Exclusion selection to First-Run Setup and Apply Optimal Settings after the initial homepage HTML + CSS warm. Use the existing compact CSS profiler source-byte inventory, consider only sources larger than 50 KB, select up to two while preferring a large theme stylesheet, append without overwriting existing exclusions, then purge and rebuild the homepage CSS bundle.
* Cap automatic setup at two total CSS Bundle Exclusion entries (including existing manual/previous entries), skip cleanly when CSS warm/profiler data is unavailable, and avoid progressively adding more exclusions on repeated Optimal Settings runs.

= 2.59.13.37 =
* Replace the always-run automatic HTML JavaScript dependency analysis in First-Run Setup and Apply Optimal Settings with hidden same-origin browser runtime checks on the homepage and up to three discovered navigation URLs.
* Preserve JavaScript safeguard lists when runtime checks capture no errors. Run Analyze HTML JS Dependencies only on pages that actually report runtime errors, apply only deterministic Strong Suggestion safeguards when available, then purge/rewarm and retest.
* Bound automatic JavaScript self-healing to three runtime verification passes, leave unresolved/non-deterministic runtime errors for manual review, and keep the normal standalone Runtime Scan and Analyze HTML JS Dependencies interfaces unchanged.

= 2.59.13.36 =
* Simplify the manual Apply Optimal Settings modal so the Overview button immediately starts website analysis and configuration in the same progress flow, with no pre-apply Detected environment or Settings applied now review boxes and no second confirmation click.
* Keep initial setup-plan failures retryable, while preserving the existing live apply/preparation progress and completion verification UI.

= 2.59.13.35 =
* Preserve already-configured Object Cache and Varnish infrastructure during First-Run Setup and Apply Optimal Settings reruns. Existing UltraCache Object Cache is runtime-verified without changing its backend, fallback, credentials, or drop-in; existing external object-cache drop-ins are preserved without UltraCache takeover.
* Add a setup-only persistence context so ordinary wizard/Optimal Settings saves skip Object Cache drop-in synchronization plus Varnish validation/rate-limit resynchronization. Fresh or unconfigured sites still use the existing Redis → APCu → SQLite → Disk Object Cache setup flow, and manual Server controls keep their existing behavior.

= 2.59.13.34 =
* Simplify Warm Cache to three scope actions (Homepage, Configured Menu, Full Site) controlled by one persistent **Also warm CSS bundles** setting, enabled by default and by Apply Optimal Settings.
* When the CSS warm option is enabled the same three actions warm HTML plus the configured CSS bundle scope; when disabled they warm HTML only. Reuse the same preference in First-Run/Optimal site preparation and JavaScript safeguard purge/rewarm, and remove the duplicate CSS-specific and restart action buttons.

= 2.59.13.33 =
* Reuse the existing Warm/Media/JavaScript ProgressPanel inside First-Run Setup and Apply Optimal Settings for live configured-menu warm and HTML JavaScript dependency-analysis progress, while suppressing only the duplicate standalone floating popup during those setup flows.
* Add phased live progress for the automatic JavaScript safeguard cache refresh (purge, homepage rewarm, configured-menu warm, and background full-site warm resume), with the menu stage automatically switching to the existing per-URL warm counters.

= 2.59.13.32 =
* Preserve server-managed HTML compression as a distinct Setup Plan result and show **Server managed** instead of **Off** in Apply Optimal Settings when the compression probe confirms that the web server or upstream layer already handles HTML compression.

= 2.59.13.31 =
* Move Activity Summary and Diagnostics from Overview to Advanced and preserve their side-by-side desktop layout.
* Open Query-string args caching by default.
* Add an Automation tab before Advanced and move Automation & Scheduling out of Cache.
* Add a Fonts & CSS tab before Media and move CSS Delivery, Fonts Optimization, and CSS Bundle Exclusions & Diagnostics there.
* Rename the Frontend tab to Javascript, keep JavaScript safeguards there, place WooCommerce beside Javascript manipulation, and simplify the WooCommerce card title.
* Restore LCP Diagnostics & Settings to Media directly above Media settings & Media Library replacement.
* Convert Advanced Frontend Javascript manipulation, Advanced CSS Delivery, and Advanced Fonts Optimisation to accordions and replace their subtitles with a red performance warning.
* Convert Quick start & examples to an Overview accordion while keeping it as the final Overview section.
* Add deliberate spacing between General Inclusions and the Advanced diagnostics area.
* Update Manual Setup/Help paths for the new Javascript, Fonts & CSS, and Automation tabs.

= 2.59.13.30 =
* Move Cache Statistics plus General Exclusions and General Inclusions to the Advanced tab.
* Move Support this plugin to Overview and keep Quick start & examples as the final Overview section.
* Place Activity Summary and Diagnostics side by side in Overview while keeping Advanced Diagnostics in Advanced.
* Move First-party parallel execution, Third-party parallel execution, LCP Boundary Delay, and Delay non-critical/local JS into a dedicated Advanced Frontend Javascript manipulation card.
* Move Inline CSS Bundling, Async Remaining CSS, and Aggressive Async CSS into a dedicated Advanced CSS Delivery card.
* Move Async Generated Font-Mix CSS Bundle and Advanced Runtime Font CSS Rewrite into a dedicated Advanced Fonts Optimisation card.
* Restyle the primary dashboard tabs to match the square card treatment, including the native white-card appearance, and add controlled spacing below the tab bar.
* Add breathing room between Server integration cards, especially Object Cache / Varnish and External Cache Flush, and normalize diagnostic-card/group spacing.
* Update Help guidance for Delay non-critical/local JS to its new Advanced tab location.

= 2.59.13.29 =
* Keep the detailed Help setup workflow as a Manual Setup alternative instead of removing it after the First-Run Wizard and Apply Optimal Settings automation.
* Rewrite the Manual Setup checklist around the six-tab dashboard, with explicit paths for HTML compression, warm menu/sources, media formats and conversion testing, fonts, WooCommerce, MailerLite, Object Cache, Varnish, and warm automation.
* Add a Manual preparation section with the new Overview / Cache / Media locations for Flush All, homepage/menu warm-up, existing-media conversion, and full-site Warm Site.
* Update post-install JavaScript, CSS diagnostics, and query-string caching guidance to point to their current Frontend and Cache tab locations.
* Preserve the manual JavaScript troubleshooting loop with the new Frontend → JS Safeguards, Overview → Flush All, and Cache → Warm Cache paths for users who configure or debug without the automatic assistant.

= 2.59.13.28 =
* Reorganize the UltraCache dashboard into six primary tabs: Overview, Cache, Frontend, Media, Server, and Advanced.
* Add addressable `?tab=` dashboard URLs with browser Back/Forward synchronization so administrators can link directly to a subsystem without introducing separate admin pages.
* Keep existing cards and running job engines mounted while tab content is hidden, so warm-up, media conversion, setup, and diagnostics work is not interrupted by navigation.
* Add Overview quick actions for Apply Optimal Settings, Flush All Cache, and full-site Warm Site, alongside cache statistics and recent activity.
* Group page-cache/warm/query/exclusion/automation controls under Cache; JavaScript/CSS/fonts/LCP/WooCommerce under Frontend; media conversion and Media Library Replacement under Media; Object Cache/Varnish/LiteSpeed/OPcache/APCu/Nginx under Server; and diagnostics/profiler/testing/import-export/support under Advanced.
* Add responsive horizontal tab navigation for narrow WordPress admin layouts while preserving the existing card and accordion design inside each subsystem.

= 2.59.13.27 =
* Integrate the existing resumable Analyze HTML JS Dependencies workflow into both First-Run Setup and Apply Optimal Settings after automatic website preparation.
* Automatically apply only deterministic Strong Suggestions, respecting each scanner `preferredTarget`: use Defer Instead when sufficient, escalate to Do Not Defer or Delay when exclusion is required, and remove overlapping Defer Instead entries during that escalation.
* Run a bounded correction loop of scan → deterministic safeguard apply → cache purge / homepage and menu rewarm / full-site warm resume → rescan, with at most three correction passes and a final verification scan after the third applied pass.
* Add persistent singleton `javascript` and `verify` wizard stages so refreshes resume the correct setup phase and first-run setup is not marked complete until the administrator confirms the final visible-interaction check.
* Keep browser-runtime/console diagnostics and generic heuristic dependency suggestions manual; automatic setup never applies speculative JavaScript exclusions.
* Normalize successful setup progress state from the engine-level `done` value to the modal `complete` presentation state so finished Flush/Warm/JavaScript steps render with their completed checkmark instead of appearing to remain active.

= 2.59.13.26 =
* Automatically prepare the website after Apply Optimal Settings: run the existing Flush All pipeline, warm the homepage, warm the configured deterministic menu when available, and start or resume the existing background full-site warm orchestrator.
* Prepare existing Media Library conversion without resetting prior work by continuing the existing media queue rebuild/repair state and resuming background media processing.
* Extend the fresh-install wizard state with a singleton `prepare` step so configuration and preparation progress survives refreshes without adding job IDs, generations, or a parallel setup queue.
* Keep setup orchestration on the existing cache, warm-up, and media engines; no second warm or conversion implementation is introduced.

= 2.59.13.25 =
* Add a fresh-install-only First-Run Setup Wizard that opens automatically on the first UltraCache dashboard visit and never auto-opens for upgrades or existing installations.
* Persist one small wizard state (`status`, `current_step`, `completed_steps`, `last_error`, `updated_at`) so welcome/analyze/configure progress survives dashboard refreshes without job IDs, generations, or setup history.
* Reuse the existing read-only Setup Plan and Apply Optimal Settings engine inside the wizard instead of introducing parallel detection or configuration logic; an interrupted configure step can be resumed by safely rerunning the idempotent setup engine.
* Keep Not now non-destructive: closing the wizard does not mark setup complete, while a successfully applied configuration records the singleton wizard as completed and prevents future automatic opening.

= 2.59.13.24 =
* Make Apply Optimal Settings run the existing Media Library conversion test automatically and choose image formats from real sample results: prefer AVIF with WebP fallback when both pass, use AVIF with original fallback when only AVIF passes, use WebP when AVIF fails, and enable Convert new uploads only after a successful tested format.
* Preserve the existing image-format/upload-conversion settings when the Media Library test is indeterminate, while applying the deterministic media defaults of Compact compression and a 1920-pixel maximum upload side.
* Enable Local Google Fonts Optimization, Bundle Generated Font-Mix CSS, and Delay icon fonts as part of Optimal Settings.
* Apply WooCommerce empty-cart suppression and MailerLite lazy nonce refresh conditionally from the reviewed Setup Plan only when those integrations are detected; keep them disabled when not applicable.
* Update the Apply Optimal Settings review/progress UI and installation Help so media, font, and conditional integration steps are no longer presented as manual post-install work.

= 2.59.13.23 =
* Make Apply Optimal Settings automatically configure the deterministic site warm-up scope from the reviewed Setup Plan: select a clear primary/main/header frontend menu at Depth 1 and save Homepage / blog index, Selected menu URLs, Pages, Posts, and Categories when available.
* Preserve the existing warm-up menu selection when multiple assigned menus have no deterministic primary signal or no assigned frontend menu exists, instead of guessing or overwriting a user choice; still configure the deterministic non-menu full-site sources.
* Show the recommended warm menu and full-site sources in the Apply Optimal Settings review modal and add a dedicated warm-scope progress phase during Apply.
* Keep scheduled/cron warm limits and automation switches unchanged in this release; only the site-specific menu/depth/source scope is automated.

= 2.59.13.22 =
* Make Apply Optimal Settings run the existing live HTML Compression capability probe automatically, keep UltraCache compression off when the server already handles delivery, prefer Brotli then gzip when UltraCache must compress HTML, and preserve the current compression setting when the loopback result is indeterminate instead of guessing.
* Harden the compression probe contract by recording whether both Brotli and gzip loopback challenges completed, so failed/incomplete loopbacks are no longer treated as proof that the server is uncompressed.
* Complete automatic Object Cache setup with deterministic Redis → APCu → SQLite → Disk capability checks, drop-in configuration through the existing settings transaction, and a fresh-request runtime payload verification; restore the previous Object Cache configuration when final verification fails.
* Keep persisted Redis credentials and externally managed secrets out of client-side Optimal Settings capability probes so redacted password fields cannot override the real server-side credential during detection.

= 2.59.13.21 =
* Add a review-first Apply Optimal Settings modal that loads the read-only Setup Plan before any settings are changed and clearly separates detected environment capabilities from the configuration applied by the current Optimal Settings recipe.
* Show live setup-plan status for server, Object Cache candidate, compression, media encoder candidates, integrations, and primary-menu selection, while marking capabilities that still require later live validation instead of presenting them as already configured.
* Add phase-aware Apply progress for recommended settings, Object Cache verification/configuration, append-only JavaScript dependency safeguards, and completion; keep the modal open with explicit success or failure state instead of applying directly from the dashboard card.

= 2.59.13.20 =
* Add a read-only Setup Detection / Planning Engine with a dedicated REST plan endpoint for server, object-cache, compression, media, integration, external-cache, menu, and warm-up capability mapping.
* Produce deterministic recommendations for Object Cache priority, compression verification, media format candidates, conditional WooCommerce/MailerLite setup, primary-menu selection, and default warm-up sources without applying or persisting UltraCache settings.
* Keep live compression, Object Cache, and image-conversion verification explicitly pending for the later setup stages instead of guessing from extension availability alone.

= 2.59.13.19 =
* Replaced the settings Profile presets with a single Apply Optimal Settings workflow based on the former Aggressive configuration.
* Removed All Off, Safe, Balanced, Aggressive, Custom matching/preset UI and their settings-profile selection logic.
* Apply Optimal Settings preserves infrastructure credentials, schedules, exclusions, safeguards, and user-maintained lists while keeping append-only Defer dependency defaults and deterministic Object Cache detection.

= 2.59.13.18 =
* Fix the WordPress 6.7+ early translation-loading notice by moving the public warm-runtime upgrade reset from plugins_loaded to early init, so UltraCache translations are not triggered before the supported lifecycle point.

= 2.59.13.17 =
* Fix the PHP 8.4 implicit-nullable deprecation in the Media Conversion Test REST endpoint by declaring its optional WP_REST_Request parameter explicitly nullable.

= 2.59.13.16 =
* Make Media Library Replacement tables explicitly use InnoDB instead of inheriting the server default storage engine, and keep the two widest reference indexes below the 1000-byte MyISAM key ceiling by storing table identifiers at their 64-character database limit and indexing the first 160 characters of row identity.
* Replace the schema-upgrade SQL MD5() backfill with bounded PHP md5() batches so replacement schema setup remains compatible with database servers where the legacy SQL MD5 function is unavailable.

= 2.59.13.15 =
* Make Strong lifecycle suggestions emitter-timing-aware: classify strong event emissions as immediate, DOM-ready, window-load, deferred callback, unknown, or mixed; suppress callback/unknown/mixed races, keep Delay-to-Defer recommendations for DOM-ready/window-load emitters, and escalate truly immediate blocking emissions to Do Not Defer or Delay.
* Persist emitter timing evidence in the per-file JavaScript analysis cache with a parser-version bump, and fix blank emitter labels to fall back to script id/source when the WordPress handle is empty.

= 2.59.13.14 =
* Remove the obsolete 80-file content-analysis ceiling from the resumable Analyze HTML JS Dependencies workflow so every prepared local JavaScript candidate is processed before dependency evidence is correlated.
* Keep each iteration bounded to at most 10 JavaScript files and the existing ~1.8 second runtime budget, preserving resumable execution without truncating larger page inventories.
* Keep Strong Suggestion heuristics, stable evidence identity, exact safeguard actions, persistent analysis cache, resume/integrity checks, progress popup, and Help guidance otherwise unchanged.

= 2.59.13.13 =
* Fix resumable Analyze HTML JS Dependencies evidence correlation to bind lifecycle findings to a stable per-script identity instead of persisted numeric inventory indexes, preventing stripped inline-script bodies from compacting the inventory and assigning listener/emitter evidence to the wrong script. Older in-flight evidence jobs are rejected and must be rescanned.
* Make exact Strong Suggestion actions write exactly the displayed suggested handle/path to Defer Instead or Do Not Defer or Delay; plugin/theme path expansion is now reserved for Defer Chain and Exclude Chain actions.
* Keep Strong Suggestion heuristics, batching, persistent analysis cache, progress popup, and the 2.59.13.12 Help guidance otherwise unchanged.

= 2.59.13.12 =
* Add a post-install Help subsection beneath the JavaScript console-check steps for cases where the Console is clear but page functionality still fails, directing administrators to Analyze HTML JS Dependencies and warning against blindly applying every suggested safeguard.
* Keep the JavaScript analyzer, Strong Suggestions heuristics, batching, cache, resume, progress popup, and safeguard behavior unchanged.

= 2.59.13.11 =
* Fix Analyze HTML JS Dependencies settings-integrity fingerprinting to read the canonical UltraCache runtime settings array instead of the REST settings response object, eliminating the array_key_exists() type error introduced in 2.59.13.09; also allow the saved scan URL to be restored during dashboard-refresh resume without reassigning a const.
* Surface the resumable HTML JS dependency scan through the shared Warm/Media progress popup, with phase, page-script, local-file, processed-file, persistent-cache-hit, and freshly parsed-file progress while retaining the existing inline diagnostic status.
* Keep the 2.59.13.10 Strong Suggestions heuristics, batching, persistent per-file evidence cache, resume behavior, and settings-change invalidation semantics unchanged.

= 2.59.13.10 =
* Harden Analyze HTML JS Dependencies Strong Suggestions so only deterministic execution-order contradictions are promoted: delayed listeners/providers must be provably later than blocking/deferred counterparts, while uncertain async races remain outside the focused Strong stream.
* Suppress lifecycle self-correlation for duplicate inventory representations of the same script, and merge multiple independent lifecycle/dependency signals for one target into a single ranked Strong Suggestion with an evidence count instead of duplicate findings.
* Prioritize unsafeguarded multi-signal findings, keep lifecycle evidence ahead of declared-dependency-only evidence, and cap the focused Strong result set at eight actionable scripts without changing the broader Runtime/Console diagnostic heuristics.

= 2.59.13.09 =
* Add phase-aware Analyze HTML JS Dependencies progress reporting with page-inventory, local-file, processed-file, persistent-cache-hit, and freshly parsed-file counters while preserving the bounded 2.59.13.08 evidence pipeline.
* Persist a browser-session resume hint for the active HTML dependency job and automatically continue the same server-authoritative job after an administrator dashboard refresh; the existing same-URL server deduplication remains the fallback when browser session state is unavailable.
* Bind every scan to a fingerprint of the JavaScript execution-order settings used to build its page profile and inventory, revalidate that fingerprint during batches and before publishing Strong Suggestions, and stop with an explicit fresh-scan requirement if the configuration changes mid-run.

= 2.59.13.08 =
* Consolidate the resumable Analyze HTML JS Dependencies path around one compact per-script evidence registry instead of persisting separate listener/emitter maps, so every readable local JavaScript file contributes one normalized evidence record from either the persistent cache or the bounded scanner.
* Build final lifecycle correlation and Strong Suggestions from that shared registry without reopening or reparsing JavaScript files, while retaining the existing declared WordPress dependency-edge analysis from the same page inventory.
* Keep 2.59.13.07 in-flight scan compatibility by translating the previous persisted listener/emitter maps at correlation time, and expose single-pass evidence counters in the completed diagnostic result without changing suggestion heuristics.

= 2.59.13.07 =
* Add persistent per-file lifecycle-analysis caching for Analyze HTML JS Dependencies using UltraCache revisioned state storage, keyed by normalized local file path and validated by file mtime, filesize, and parser version.
* Reuse only compact extracted listener/emitter evidence for unchanged local JavaScript files; never persist JavaScript bodies, and automatically reanalyze and replace a cache row when the source fingerprint changes.
* Track cache hits, misses, and writes in the resumable scan result, and clear the JS analysis cache at the canonical Flush All clean-slate boundary while retaining the 2.59.13.06 batching and 2.59.13.05 Strong Suggestions behavior.

= 2.59.13.06 =
* Make Analyze HTML JS Dependencies use a dedicated resumable action job so page preparation, bounded local-JavaScript analysis batches, and final evidence correlation no longer run as one monolithic Strong Suggestions request.
* Process at most 10 readable local JavaScript files per lifecycle-analysis iteration with a short runtime budget, persist the cursor plus compact listener/emitter evidence between requests, resume the same URL job after an interrupted dashboard session, and skip the legacy monolithic JS safety file pass during this dedicated scan.
* Keep the 2.59.13.05 Strong Suggestions heuristics and output contract unchanged, avoid the global heavy-action lock for the resumable analyzer, and keep inline script bodies out of persisted scan state.

= 2.59.13.05 =
* Add a focused Strong Suggestions stream to Analyze HTML JS Dependencies so high-confidence silent Delay/Defer execution-order conflicts are surfaced separately from broad compatibility heuristics.
* Extend local JavaScript lifecycle analysis to resolve generic event-dispatch wrapper methods that forward literal lifecycle names into dispatchEvent(new CustomEvent/Event(...)), allowing missed one-time initialization events to be detected without hardcoded plugin exceptions.
* Prefer the exact WordPress script handle for Strong Suggestions when available, deduplicate by script, prioritize lifecycle conflicts, and cap the focused result set so actionable findings are not buried under low-value matches.

= 2.59.13.04 =
* Update the WordPress.org Donate link to the UltraCache page at https://iniotakis.com/ultracache/.
* Update installation step 14 to enable Warm uncached URLs after first visit under Warm Cache.

= 2.59.13.03 =
* Extend Console Error Handler diagnostics with the actual WordPress script dependency graph from the selected page and compare each declared edge against the final Delay/Defer/blocking strategy applied by UltraCache.
* Inspect readable same-site JavaScript files for lifecycle listener/emitter relationships so one-time initialization order risks can be proposed even when the browser produces no console error.
* Keep an exact script visible when the same runtime error persists after it is already listed in Do Not Defer or Delay; distinguish a safeguard that is still optimized in final HTML from a fully blocking script whose dependency chain still needs investigation.
* Add dedicated dependency-risk and persistent-failure diagnostic buckets without adding hidden compatibility exceptions or changing either safeguard list automatically.

= 2.59.13.02 =
* Add per-page Elementor generated-CSS dependency reconciliation to the shared final cache-storage path. UltraCache dynamically discovers Elementor `post-{id}.css` references in each rendered page, regenerates only missing referenced files through Elementor's own `Post_CSS::create($post_id)->update()` contract, verifies the physical file, and refreshes the dependency version before page-cache storage.
* Apply the same dependency contract to normal frontend STORE requests and shared warm writes. Unresolved Elementor CSS dependencies prevent UltraCache from storing that broken HTML variant instead of publishing a cache entry that references missing generated CSS.
* Keep Elementor cache ownership with Elementor: no global Elementor CSS regeneration is introduced. Optional Elementor Flush All still uses Elementor's native clear; CSS is rebuilt per page as each URL is visited or warmed.
* Correct Elementor help/FAQ text so Element Cache expiration is described as element-output lifetime, not generated-CSS lifetime.
* Make the two atomic schema-install `add_option()` calls use the explicit prefixed `ultracache_locks_schema_install_lock` option name so WordPress.org static review can verify the namespace without changing lock semantics.

= 2.59.13.01 =
* Fix the first-visit background warm runtime setting lookup so the enabled dashboard switch is read through the canonical snake_case runtime settings contract after a successful frontend STORE.
* Preserve the existing post-response enqueue boundary, shared targeted warm pipeline, Background warm pages per minute rate, and configured Varnish/LiteSpeed warm stages; no options or queue semantics are changed.

= 2.59.12.99 =
* Detect active Elementor as a supported native cache layer and add an opt-in Also flush Elementor Cache switch under External Cache Flush.
* Clear Elementor through its native files manager before UltraCache removes page cache, suppress the resulting self-originated Elementor callback, and let existing warm-after-flush work start only after both cache layers are invalidated.
* Add FAQ guidance recommending a 1 Year Elementor Element Cache expiration for mostly static sites without changing Elementor settings automatically.

= 2.59.12.98 =
* Defer Elementor generated-file invalidation reconciliation until shutdown so multiple clears in one request coalesce after the final clear, then run one canonical UltraCache Flush All and the existing configured warm behavior.
* Add narrowly scoped PHPCS SchemaChange ignores, with explicit versioned custom-table index-repair justification, to the eight intentional legacy index migration statements.

= 2.59.12.97 =
* Flush the canonical UltraCache page-cache layers when active Elementor clears its generated public files, preventing cached HTML from retaining references to removed Elementor CSS files.
* Reuse the existing Flush All warm behavior after Elementor invalidation; no Elementor-specific warm queue or crawler is added.
* Remove the Elementor Element Cache FAQ recommendation because disabling that separate cache does not prevent Elementor generated-file invalidation.

= 2.59.12.96 =
* Replace dynamic media queue and Media Library Replacement blocker `IN (...)` SQL construction with bounded 20-row batches whose complete placeholder sets are declared directly in each prepared query.
* Pass every affected prepared statement directly to the matching `$wpdb` read/write method, eliminating ambiguous intermediate query variables and replacement-count reports without suppressing database checks.
* Preserve the existing bulk operation semantics, table-identifier validation, blocker decisions, queue resets, cleanup, and readiness diagnostics.

= 2.59.12.95 =
* Align the runtime `ULTRACACHE_VERSION` constant with the installed plugin version so administrator JavaScript and CSS assets use the current cache-busting query value.
* Ensure the corrected automatic warm-up popup guidance is loaded immediately after upgrading instead of reusing the cached 2.59.12.93 `ui.js` asset.

= 2.59.12.94 =
* Correct the setup help popup so automatic warm-up guidance points to Warm full site after Flush All Cache and/or Warm uncached URLs after first visit instead of the unrelated scheduled-cleanup option.

= 2.59.12.93 =
* Add an optional Warm uncached URLs after first visit switch that queues the first cold frontend URL into the shared background warm pipeline, coalesces with on-visit CSS bundle work, uses the configured Background warm pages per minute rate, and completes the same enabled external-cache stages as full-site warming.
* Restore a locked LCP mapping as an immutable terminal boundary: missing request-credentials metadata no longer reopens frontend observation for that viewport.
* Remove the credentials-only update path that could modify a locked LCP row and trigger a targeted purge or LCP refresh without an explicit Forget mapping or Relearn action.
* Keep runtime image request credentials observation inside the normal Learning cycle only; existing locked mappings with an unknown mode remain unchanged until the administrator explicitly starts a new learning cycle.

= 2.59.12.91 =
* Extend the existing frontend LCP discovery cycle, only when Fix sliders / hero sections and LCP Image Priority are active, to observe the actual runtime image request credentials mode used for the LCP image URL.
* Suppress speculative slider/hero image preloads while the credentials mode is unknown; after browser observation, persist `none`, `anonymous`, or `use-credentials` and refresh only the affected page through the existing targeted LCP refresh queue.
* Emit browser-observed image preloads with the exact persisted `crossorigin` contract, skip preloading when the runtime mode is unavailable or conflicting, and keep non-slider pages on their existing preload behavior.
* Reopen legacy locked LCP mappings only for their missing credentials field, without relearning the LCP resource or triggering a full-site flush/warm cycle.

= 2.59.12.90 =
* Add FAQ guidance to disable Elementor Element Cache when UltraCache Page Cache is active, while keeping Elementor CSS Print Method on External File and running one Clear Files & Data, Flush All, and Warm Cache cycle after the change.
* Add FAQ guidance for strict color fidelity: disable Ignore color profile preservation; images whose embedded profile cannot be verified or preserved may remain in their original JPG/PNG format instead of being converted to AVIF/WebP.
* Enable Ignore color profile preservation by default in the Aggressive performance profile to maximize AVIF/WebP conversion coverage; Safe, Balanced, and All Off retain strict profile preservation.

= 2.59.12.89 =
* Remove the canonical Media Library Replacement singleton workflow option during dashboard Delete All cleanup and normal WordPress uninstall.
* Repeat deletion of the singleton workflow option in the uninstall final cleanup pass so a loaded shutdown callback cannot leave completed replacement state behind after the custom tables are dropped.
* Prevent a clean reinstall from displaying stale Preparation Complete, Replacement Applied, Verify, or Delete state from a workflow that belonged to the removed installation.

= 2.59.12.86 =
* Record resumable Theme CSS checksum validation with the canonical `validated` status instead of the incompatible `seen` value.
* Keep post-apply Theme CSS inventory persistence on the same canonical validation status so the inventory has one status contract.
* Remove the false "Theme CSS files disappeared" failure caused by the validator writing a status that the final file-set parity query did not recognize.

= 2.59.12.85 =
* Count deferred blocker sibling rows as part of the same canonical registry inventory, keeping registry/readiness parity stable after an affected attachment moves from matched to blocked_dependency.
* Persist registry completion as a durable singleton workflow milestone with the validated registry and readiness variant counts.
* Prevent a later Prepare request from repeating the initial registry consistency phase after blocker deferral; resume directly from file-copy planning without jobs, generations, or alternate state sources.

= 2.59.12.84 =
* Preserve the completed singleton readiness inventory when Prepare initializes the replacement registry instead of replacing the canonical readiness section with an empty default array.
* Make the final Prepare consistency check compare registry variants against the same persisted readiness snapshot that authorized Prepare, fixing false registry/readiness results such as 6654/0 after a complete scan.
* Keep readiness and Prepare in one canonical workflow option without reintroducing jobs, generations, fallback state sources, or browser-side workflow selection.

= 2.59.12.83 =
* Make deletion of Prepare-created replacement destinations best-effort during Restart so an unlink failure cannot block the authoritative reset of the one Media Library Replacement workflow.
* Continue clearing all six replacement tables, blocker decisions, plans, cursors, confirmations, and canonical workflow state after best-effort destination cleanup; the next Prepare re-evaluates any remaining file through the normal filesystem plan.
* Preserve the hard Restart boundary after destructive Do work has started and keep overwrite-backup restoration fail-closed.

= 2.59.12.82 =
* Make Restart Replacement Plan an authoritative reset of the one Media Library Replacement workflow without publication-contract versions, destination-ownership proofs, job provenance, or legacy-plan compatibility gates.
* Restore overwrite backups and remove destinations recorded as created during the current Prepare directly from the singleton registry, then clear all replacement tables, blocker decisions, plans, cursors, confirmations, and persisted workflow state.
* Preserve the existing block after destructive Do work has started, where rollback evidence is still required before any reset.

= 2.59.12.81 =
* Replace the Media Library Replacement execution-instance model with one canonical persisted workflow state shared by Prepare, blocker decisions, Do, Verify, Delete Originals, recovery, and status polling.
* Remove functional job IDs and generations from replacement REST requests, responses, SQL selection, React state, pagination, previews, CLI operations, and destructive authorization; retain legacy schema columns only as blank compatibility fields until a later schema-only cleanup.
* Make Restart clear all current replacement rows, blocker decisions, database/file/metadata/Theme CSS plans, nested cursors, confirmations, and workflow state, while preserving rollback evidence by refusing an unsafe reset after destructive Do work has begun.
* Make the blocker badge and atomic modal consume the same singleton backend truth, and isolate database, cleanup, and Theme CSS previews from authoritative workflow status.
* Persist readiness, database indexing, intermediate expansion, Theme CSS scanning, streaming, validation, and resumable cursors inside the canonical state; bind just-in-time destructive confirmation to the current plan fingerprint so cross-session continuation does not depend on browser or token lifetime.
* Preserve the bounded database reference extractor and inline data-URI exclusion introduced in 2.59.12.77 without changing its implementation bytes.

= 2.59.12.80 =
* Replace the separate Decide Blockers payload and open flags with one atomic modal state so the verified blocker response and the visible modal are committed in the same React state transition.
* Let the blocker endpoint select the server-active replacement job on initial open, lock pagination to the returned job, validate the response contract, and refuse to render an open modal without a real payload.
* Stop database, Theme CSS, and cleanup preview responses from overwriting the authoritative Media Library Replacement workflow status, and make periodic status refresh resolve the active job on the server.
* Preserve loaded blocker decisions across pagination while replacing them cleanly on the first modal load, without changing blocker rows, decisions, Prepare, Do, or database contracts.

= 2.59.12.79 =
* Remove short-lived database and Theme CSS confirmation tokens from Prepare planning validation, the final pre-Do guard, and prepared-plan validity so a completed plan remains actionable hours or days later.
* Suppress destructive token issuance from internal Prepare and recovery previews while retaining fresh confirmation tokens for explicitly invoked standalone preview/apply endpoints.
* Issue and consume a fresh database or Theme CSS start token inside the first authenticated Do chunk for that phase, then persist durable job/generation authorization for every later resumable chunk.
* Allow paused Do recovery to resume after the token TTL or from a later authorized administrator session without weakening registry, destination-file, metadata, database, Theme CSS, generation, or fingerprint guards.

= 2.59.12.78 =
* Make the authoritative active workflow status job ID take precedence over stale mapping, database, blocker, or cleanup preview job IDs when dashboard actions resolve the current Media Library Replacement job.
* Clear blocker payloads, decisions, and every replacement preview/modal state after Restart Replacement Plan so a previous job cannot remain attached to a newly prepared job in React state.
* Replace blocker decisions on the initial modal load and merge them only during pagination, preventing React batching from restoring decisions retained by an earlier job.
* Reject blocker responses whose returned job ID differs from the requested active job instead of presenting an empty Decide Blockers modal against the wrong replacement plan.

= 2.59.12.77 =
* Replace whole-value global PCRE image-reference extraction with one deterministic bounded byte scan, preventing large inline data-image payloads from trapping Media Library Replacement Prepare in CPU-bound backtracking after nginx has returned 504.
* Ignore inline data URIs because they are embedded payloads rather than replaceable Media Library files, while retaining exact raw database fragments for Apply and Verify.
* Reuse the existing reference normalization, uploads-path resolution, prepared database index, matching, Apply, and verification contracts without changing SQL pagination, escaping, blocker decisions, or adding development-version migration code.
* Preserve existing reference coverage for HTML, CSS url(), absolute and protocol-relative URLs, JSON-escaped slashes, query strings, fragments, and srcset-style values without creating a second full unescaped copy of each database value.

= 2.59.12.76 =
* Replace unbounded leading-wildcard Media Library Replacement database candidate searches with adaptive, bounded primary-key windows; image predicates execute only inside the resolved row range, while unsupported table shapes retain bounded raw-row OFFSET traversal.
* Make reference-index replay idempotent with INSERT IGNORE, advance progress by physical rows scanned rather than matching references found, keep database-match work out of the denominator until indexing completes, and expose the current table, column, cursor, scanned rows, and query duration.
* Continue the complete non-destructive Prepare discovery and planning flow when blocker rows exist. Defer every sibling row of an affected attachment to prevent partial file or metadata plans, then open Decide Blockers only after database and Theme CSS planning completes.
* Finalize group and per-file blocker decisions without starting Apply: whole-attachment exclusions proceed directly to the final pre-Do guard, while verified-overwrite decisions reactivate only affected attachments and rebuild their file, database, and Theme CSS plans.

= 2.59.12.73 =
* Stop the Prepare dashboard runner immediately when the server enters decisions_required, close the active progress panel, release the Prepare lease, and expose Decide Blockers without repeatedly calling the same Prepare phase.
* Remove redundant per-chunk replacement status GET requests from Prepare and treat the final status refresh as non-fatal, preventing transient nginx/PHP-FPM 502 responses from turning persisted Prepare progress into a dashboard failure.
* Add per-file blocker decisions beneath each group policy. Group decisions provide the scalable default, while paginated individual rows can override that policy before one atomic save.
* Keep Save Decisions limited to decision persistence and Prepare completion. Apply remains disabled until Prepare finishes and always requires a separate explicit click.

= 2.59.12.72 =
* Add a Prepare → Decide Blockers → Apply Media Library Replacement workflow: Prepare freezes actionable conversion and destination blockers instead of treating them as technical Prepare failures, and Apply remains locked until every blocker group is resolved.
* Persist blocker code, detail, generation, group decision, administrator, and decision time on the prepared physical registry; support keeping affected attachments as originals and overwriting different existing destinations through the existing verified backup contract.
* Add a grouped blocker decision modal using the existing replacement-preview layout, summary cards, server-side pagination, group-level actions, and per-unit details so large libraries require one decision per blocker class instead of one decision per image.
* Remove the global Existing destination files policy from the main Media Library Replacement settings; identical verified destinations remain automatic, while actual collisions are discovered by Prepare and presented only when they exist.
* Exclude keep-original attachments from file copy, metadata planning, database and Theme CSS matching, verification, and original deletion while retaining them in the frozen decision audit trail.

= 2.59.12.71 =
* Make replacement readiness prefer an authoritative persisted semantic skip for an exact physical source/format over stale pending duplicates from compatible parent requests.
* Carry physical-unit skip reason and detail through readiness blocker signatures, responses, progress messages, and dashboard diagnostics so deterministic unsupported variants report their real conversion blocker.
* Prevent replacement readiness from requeueing an exact unit already resolved as a terminal semantic skip while continuing to block Prepare until the required target file can actually be generated.

= 2.59.12.70 =
* Persist deterministic physical-unit semantic skips with their exact reason, detail, source fingerprint, and conversion-context signature so missing targets do not reopen unchanged skipped work.
* Reattempt a persisted semantic skip only after the source descriptor/fingerprint changes, the conversion context changes, or an explicit regeneration resets the unit.
* Reconcile terminal attachment parents that still contain nonterminal child units and derive completion from the authoritative child aggregate instead of leaving skipped/done parents with pending units.
* Stop WP-CLI media processing after repeated identical persisted queue signatures, preventing parent/unit attempt counters from masking a no-progress state-machine loop.

= 2.59.12.69 =
* Restored the intermediate attachment metadata verification helper used by replacement Verify and cleanup inspection after the attachment-level metadata apply refactor.

= 2.59.12.68 =
* Apply Media Library Replacement attachment metadata through one authoritative old-to-final plan per attachment instead of separate main-image and intermediate-size writes to the same serialized metadata value.
* Add an additive per-attachment metadata-plan registry that preserves active version 13 jobs, materializes plans during Prepare, and lazily materializes plans when resuming jobs created by earlier builds.
* Fix false Do consistency failures where UltraCache interpreted its own already-applied thumbnail metadata changes as external attachment changes.
* Support deterministic recovery from persisted partial states created by older runs while continuing to reject unknown filename, MIME, metadata, or custom-field changes made after Prepare.
* Count metadata Do progress by attachments with a stable total while retaining per-file registry rows for file verification, reference replacement, diagnostics, and rollback evidence.

= 2.59.12.67 =
* Keep the failed Prepare Replacement button disabled while replacing its technical-only tooltip with the actionable recovery path: open Advanced / Manual Recovery, run “Restart Replacement Plan”, and then run “Prepare Replacement” again.
* Display the same Prepare failure recovery guidance persistently below the disabled button so the next action remains visible without mouse hover or on touch devices, while leaving all Manual Recovery logic and enablement unchanged.

= 2.59.12.66 =
* Make Media Library Replacement Prepare consistency authoritative to persisted registry rows and readiness unique variants instead of transient scan-level skipped counters, preventing attachments without intermediate metadata or ignored non-physical metadata slots from falsely failing Prepare.
* Treat absent intermediate sizes and invalid non-physical metadata slots as no-work inventory entries rather than replacement blockers; continue to persist and block on real missing/unreadable source files, invalid generated outputs, registry skipped rows, insertion failures, and registry/readiness mismatches.
* Report registry skipped/failed rows and scan insertion failures explicitly in Prepare consistency errors so a server-backed failure identifies the authoritative blocker class.
* Keep the readiness progress popup open after terminal conversion blockers and append the requested recovery action: run “Scan Media Library & Repair Status” and rerun “Start / Resume Conversion”.

= 2.59.12.65 =
* Reconcile attachment parent state immediately after cooperative interruption, administrator pause, Retry Failed, fatal-shutdown recovery, and stale-worker recovery so dashboard, WP-CLI, readiness, and background workers resume from the same persisted physical-unit aggregate.
* Make exact child-unit shutdown recovery authoritative for the parent recovery status and stale-recovery generation, preventing parent and main/thumbnail retry budgets from diverging after a terminated worker.
* Clear obsolete encoder-attempt evidence whenever a unit is returned by a worker-level pause, interruption, or stale recovery, preserving the exact worker failure reason instead of displaying encoder diagnostics from an earlier conversion attempt.
* Resume background dispatch after a dashboard manual session ends when attachment work, local assets, physical units, queue build, or lazy unit inventory still require progress; do not depend only on attachment-level pending rows.
* Harden cross-surface recovery contracts for UI-to-WP-CLI and WP-CLI-to-UI handoff, output-written-before-claim-loss reconciliation, terminal retry limits, legacy false-done repair, metadata superseding, and independent AVIF/WebP unit state without changing the parent or child schemas.

= 2.59.12.64 =
* Stop Prepare Replacement readiness deterministically when a complete pass still has queueable variants but produces no enqueue, physical-unit attempt, generated output, unit-state transition, or blocker-set change; complete with blockers instead of starting an unbounded verification pass.
* Persist a deterministic blocker signature across readiness chunks and passes, including attachment, physical file, status, attempts, failure code/stage/detail, so repeated unresolved state is observable and resumable without arbitrary pass limits.
* Read readiness state from exact physical AVIF/WebP child units across compatible exact, best, and both parent policies, preserving per-file failure details and preventing attachment-level success from hiding a failed main image or thumbnail.
* Report the first exact blocked attachment, main/intermediate source path, persisted failure code, and failure detail in the readiness response and dashboard while keeping Media Library replacement locked until every required output is valid and current.

= 2.59.12.63 =
* Add authoritative physical-unit status counters joined to attachment parent policies, including materialized-parent coverage, pending/processing/done/failed/skipped units, retry-pending units, required totals, outstanding work, and completion that remains false while lazy inventory coverage is incomplete.
* Separate legacy parent-row touches from completed attachments and physical-unit attempts in batch results; report attachment/local-asset terminal outcomes, resolved/generated units, retryable unit failures, and terminal unit failures without changing attachment-level queue semantics.
* Make WP-CLI process, rebuild, process-batch, and scoped-ID output use the exact aggregate counters, remove ambiguous attachment wording, and refuse completion while either parent rows or physical units remain failed.
* Count readiness generated units only from successfully generated AVIF/WebP outputs rather than attempted units, while retaining legacy response aliases for compatibility with existing REST/background consumers.
* Keep dashboard batches, maintenance dispatch, cron fallback, and WP-CLI active while lazy physical-unit inventory is incomplete even when no attachment parent is currently pending; expose exact coverage and wait-state diagnostics instead of reporting premature completion.
* Avoid duplicate reconciliation inside one process batch, exclude terminal skipped parents that require no physical inventory from coverage debt, and report migration failures as explicit incomplete work rather than silently advancing counters.

= 2.59.12.62 =
* Route dashboard, REST, WP-CLI, Media Library Replacement readiness, and background attachment processing through one authoritative physical-unit worker; remove the CLI whole-attachment shortcut and process one persisted main/thumbnail output unit per claim.
* Persist exact per-file success, semantic skip, retryable failure, terminal failure, encoder-attempt evidence, source fingerprint, attempt count, and stale-worker recovery state before any later unit can run.
* Derive each attachment parent status only from active child-unit rows, forbid done when any required main image or thumbnail remains pending/failed, and trigger affected-page processing only after the complete parent reaches a successful terminal state.
* Extend pause, retry, explicit regeneration, cooperative interruption, fatal-shutdown recovery, and stale-claim recovery to child units while retaining the attachment-level parent queue, local-asset execution, UI attachment counters, and existing schema versions.

= 2.59.12.61 =
* Lazily materialize physical conversion units for existing attachment queue rows during rebuild, execution, Media Library Replacement readiness, and bounded administrator/CLI maintenance while leaving local-asset rows atomic and unchanged.
* Reconcile every active unit against its exact source and target files: validate format headers and freshness, mark current outputs done, keep missing/stale/invalid outputs pending, classify unsupported mappings as skipped, and supersede obsolete metadata units.
* Recompute attachment parent state from reconciled child units so a legacy done/skipped row cannot remain complete while a required main image or thumbnail is missing; preserve terminal failed parents until explicit retry unless all required outputs are already valid.
* Keep child rows synchronized after the legacy attachment worker persists a result, remove child rows with parent rebuild/clear operations, and clean bounded orphan units plus persistent migration state without changing UI attachment counters or activating child-unit claims yet.

= 2.59.12.60 =
* Add a dedicated persistent physical media conversion unit table keyed to the existing attachment-level parent queue without changing current UI, REST, WP-CLI, background-worker, retry, local-asset, or replacement execution semantics.
* Build deterministic main and intermediate source inventories that expand best/both policies into concrete AVIF/WebP units, persist exact source and target paths plus fingerprints, and remain idempotent across repeated synchronization.
* Supersede obsolete physical units when attachment metadata changes, reactivate changed source fingerprints as pending work, and retain the parent queue as the stable aggregate/request contract for the later per-unit worker migration.
* Register the new operational table with UltraCache table validation, activation schema checks, plugin-data cleanup, uninstall cleanup, and Media Library Replacement operational-state exclusion.

= 2.59.12.59 =
* Release cooperative local-asset early-return claims immediately back to pending without consuming failure budgets, and recover fatal/terminated PHP shutdown claims automatically; quarantine only repeated real worker terminations after the persistent recovery budget is exhausted.
* Make Retry Failed recover abandoned processing rows as well as terminal failed rows under the exclusive media-process lease, expose recoverable interrupted work to the dashboard, and dispatch the queue immediately after administrator recovery.
* Order pending media work by the lowest attempt count before row identity so a retried AVIF or WebP unit cannot starve another pending format for the same source.
* Add the Images & Media Library FAQ guidance for theme/plugin images that remain JPEG or PNG, while preserving original local files and the existing rewrite, encoder, replacement, and schema contracts.

= 2.59.12.58 =
* Fix local theme, plugin, mu-plugin, and WordPress-includes media jobs remaining in processing before encoder execution when the guarded source-fingerprint UPDATE matched the active claim but changed no stored values.
* Treat MySQL/MariaDB zero changed rows as a valid no-op only after an exact id, status, attempts, and started-at claim verification; retain stale-claim rejection when that authoritative claim no longer exists.
* Preserve the existing local-source resolver, generated identity, shared media queue, AVIF/WebP encoders, affected-page fence, Media Library Replacement, and schema versions without adding a migration.

= 2.59.12.57 =
* Repair the media queue schema 4-to-5 upgrade by adding source-request uniqueness only after legacy attachment rows receive their canonical source identity and requested-format values.
* Replace MariaDB-incompatible SHOW INDEX WHERE ... LIMIT probes with full index metadata inspection, and rebuild the legacy unique attachment indexes as the required non-unique lookup indexes without duplicate-key attempts.
* Make media queue and affected-page reference upgrades idempotent across fresh, legacy, and partially applied schemas, deduplicating only identical canonical work identities before creating the final unique indexes.
* Advance the media queue schema to 6 and affected-page reference schema to 3 while preserving queued attachment/local-asset work, statuses, retries, source fingerprints, and page-purge requirements.

= 2.59.12.56 =
* Complete the integrated local-image rewrite and WordPress-aware wp_options replacement regression candidate across canonical WordPress-derived source resolution, collision-free local-asset variants, the shared media queue, HTML/CSS/LCP surfaces, option identity, cache synchronization, concurrency, recovery, and rollback.
* Match native WordPress option action sequencing around the byte-exact writer: publish the generic update_option action immediately before the atomic SQL mutation, then publish the option-specific update action and updated_option only after the database and runtime cache state are verified.
* Preserve the established Media Library uploads generated identity byte-for-byte, keep Media Library Replacement scoped away from theme/plugin originals, retain schema 13 without migration, and keep local-asset work isolated from attachment rebuild and repair operations.
* Add one integrated release gate covering executable identity/freshness/option-writer behavior, source-contract parity, complete PHP syntax, release metadata, package root, archive integrity, and exclusion of development tests from the installable ZIP.

= 2.59.12.55 =
* Keep every persisted WordPress option reference on the dedicated option-aware writer even when the active multisite blog context changes after Prepare Replacement; never fall through to the generic database-row writer.
* Bind Apply, verification repair, recovery, and rollback to the exact current $wpdb->options table and fail closed with a specific context-change result before any database mutation.
* Expand executable regression coverage for autoloaded and non-autoloaded raw, serialized, and JSON-like options, alloptions/notoptions synchronization, persistent cache invalidation, filter mutation, reused IDs, multisite context drift, concurrent compare-and-swap conflicts, and rollback parity.
* Preserve Media Library Replacement schema 13 and the 2.59.12.54 option-name identity contract without adding a migration or changing non-option database replacement behavior.

= 2.59.12.54 =
* Route Media Library Replacement writes to the current WordPress options table through a dedicated option-aware compare-and-swap adapter instead of the generic database-row writer.
* Persist the exact option name with each indexed and planned option reference, block legacy or reused option IDs without matching identity, and preserve byte-exact concurrent-writer protection.
* Synchronize per-option, alloptions, notoptions, and persistent object-cache state after apply, verification repair, recovery, and rollback; verify the exact cached value before completion.
* Run WordPress option sanitization and pre-update filters before the atomic write, reject transformed plans, and publish the standard option update actions only after the database and runtime cache state are verified.
* Upgrade Media Library Replacement schema 12 to 13 additively without discarding existing registry rows; option plans created before the identity contract require a fresh Prepare step.

= 2.59.12.53 =
* Route full-document and filtered-fragment image admission through the canonical WordPress-derived local-source roots, restoring theme, plugin, mu-plugin, and WordPress-includes rewrite parity without changing Media Library Replacement scope.
* Share one side-effect-free exact-source AVIF/WebP lookup across HTML attributes, srcset, inline and external CSS, Slider Revolution image attributes, LCP candidate preference, and LCP preload equivalence while preserving existing uploads and local-asset identities.
* Extend supported semantic image surfaces with lazy srcsets, Slider image data attributes, and video posters, keep foreign/non-image/metadata/download contexts unchanged, and retain lookup-only frontend behavior with the existing local-asset queue.

= 2.59.12.52 =
* Extend the existing persistent media queue with canonical local-asset work rows for theme, plugin, mu-plugin, and WordPress-includes image sources without creating a second queue or attachment record.
* Re-resolve each local public source through the existing WordPress-derived path contract before encoding, bind work to canonical source identity plus exact requested format and source fingerprint, and keep frontend requests lookup-only.
* Reuse the established AVIF/WebP encoder, admission, color-profile, orientation, animation, atomic output, retry, stale-worker, affected-page purge, and background dispatch contracts while preserving Media Library attachment queue behavior.

= 2.59.12.51 =
* Add a separate WordPress-derived local-asset AVIF/WebP storage namespace and collision-resistant source identity for theme, plugin, mu-plugin, and WordPress-includes images.
* Preserve the established uploads-relative generated paths byte-for-byte while deriving non-upload identity from canonical source scope, owner, relative path, and full source filename.
* Let runtime public-URL lookup serve fresh existing local-asset derivatives with the same exact-source mtime/size freshness contract, while leaving non-attachment queue generation for the next roadmap version.

= 2.59.12.50 =
* Add one canonical local public image source descriptor using the existing WordPress-derived uploads, plugin, mu-plugin, theme, and WordPress-includes path resolvers.
* Route frontend AVIF/WebP public-URL admission through the shared descriptor while preserving the established uploads-relative generated identity, freshness lookup, and attachment-backed on-demand queue behavior.
* Memoize exact local source descriptors, preserve query-insensitive source identity, support custom registered theme directories, and reject foreign URLs, unsupported extensions, traversal, and UltraCache-generated cache/image outputs.

= 2.59.12.49 =
* Exclude every UltraCache-owned table and UltraCache-owned option row from Media Library replacement database discovery, planning, preview, apply, verification, and rollback.
* Add an apply-time internal-state guard so a plan created by an earlier version cannot write UltraCache operational records; existing internal plan rows are classified as excluded instead of failed.
* Add Continue Replacement recovery for failed metadata, database, Theme CSS, and post-replacement Flush All phases while preserving every completed change.
* Add Restart Database Replacement for database-phase failures, rebuilding only unresolved database references from the current database while preserving switched attachment metadata and successful content replacements.
* Run Flush All after Media Library replacement completes and report the attachment, site-content, internal-exclusion, and clean-slate cache-reset result in the completion message.

= 2.59.12.48 =
* Show homepage HTML warm completion with the canonical warmed URL and the actual HTML, Varnish-variant, and LiteSpeed pipeline stages instead of a static success sentence.
* Show homepage HTML + CSS bundle warm completion through the same stage formatter used by menu and full-site warm-up, including CSS and the verified Varnish variant count.
* Reuse the same result-driven homepage summary when a shared/homepage CSS bundle is prepared before a broader manual warm job; no warm runtime behavior is changed.

= 2.59.12.47 =
* Make Flush All a clean warm/Varnish queue generation boundary by deleting every prior pending, retrying, processing, completed, skipped, and terminal row instead of preserving invalidation and refill work.
* Clear foreground and cron ownership, bump the warm generation, unschedule old ticks, and wait for the authoritative cron worker lease to finish before deleting the old queue.
* Hold a purge mutation barrier that blocks new page-cache and generated-CSS write locks and drains existing write/build locks before the cache directory is removed.
* Start external cache clearing and the optional warm-after-flush plan only after the purge mutation barrier is released, so any resulting queue rows belong exclusively to the new generation.
* Fail Flush All when the clean-slate queue reset cannot be committed instead of reporting success with stale queue work still present.

= 2.59.12.46 =
* Allow the authenticated HTML-variant diagnostic warm pipeline to ignore only the `DONOTCACHEPAGE` flag inherited from the current UltraCache admin/REST request while preserving all public URL eligibility, exclusion, WooCommerce, and cacheability checks.
* Revise the HTML-variant capability contract so the self-blocked 2.59.12.45 observation is invalidated and cannot remain authoritative before a fresh Test Varnish run.
* Present an incomplete upstream warm/refill observation as `Observation incomplete` with its concrete stored reason instead of collapsing it into `Not supported · 0 of N`.

= 2.59.12.45 =
* Retry only transient DNS, transport, timeout, throttling, and server failures during public Varnish HTML-variant refill while stopping immediately on successful or terminal HTTP responses.
* Remove the stale `cache-write-failed` classification from successful HTML warm results and propagate the actual failed Varnish bucket, HTTP/error code, retryability, and bounded attempt history through the warm pipeline and queue result.
* Keep HTML-variant capability observation inconclusive when the canonical warm/refill path cannot reach the per-bucket proof stage instead of converting an upstream refill failure into a false variant-unsupported result.
* Pass the explicit warmed page URL, source, and Accept bucket into the final media rewrite so cron, CLI, stale, and warm workers can queue missing AVIF/WebP files and associate them with the correct page.
* Scope missing-media discovery limits per warmed page and continue affected-page registration when a missing optimized lookup is memoized or already queued elsewhere in the same worker.
* Rewrite local upload background images emitted by page builders inside inline CSS `<style>` blocks through the same validated AVIF/WebP lookup and on-demand generation path used by image markup and style attributes.

= 2.59.12.44 =
* Separate Varnish capability observation from control transport: create and verify canaries through the canonical public WordPress route while sending PURGE or BAN only to the configured control endpoint.
* Restore HTTP exact-invalidation discovery for control-only listeners such as the Hetzner endpoint without requiring those listeners to serve ordinary frontend GET requests.
* Preserve independent Exact PURGE and Exact BAN behavior probes and derive broader capability tests from the method verified during the current run.
* Preserve terminal topology outcomes such as entire-host `not-applicable` instead of reclassifying them through generic supported/unsupported aggregation.
* Persist endpoint, basic-test, HTML-variant, ESI, and aggregate capability diagnostics in existing revisioned UltraCache state records, with one-time migration from superseded diagnostic options.
* Invalidate the incorrect 2.59.12.43 capability proofs through revised capability fingerprints so they cannot authorize runtime behavior before a fresh Test Varnish run.
* Keep capability-probe operations isolated from production recent-operation reporting and require a specific reason for every non-executed or inconclusive capability result.

= 2.59.12.43 =
* Independently behavior-test HTTP Exact PURGE and Exact BAN on every configured endpoint instead of limiting probes from the configured method or adapter classification.
* Derive the common production hard-invalidation method from current endpoint proofs and use the same verified method for targeted, HTML-only, and entire-host runtime operations.
* Probe Batch BAN, HTML-only flush, entire-host flush, soft purge, stale refresh, and origin revalidation independently without executing known-URL or TTL production fallbacks.
* Persist explicit per-endpoint capability states, reason codes, messages, applicability, conclusiveness, timestamps, and proof expiry in the existing UltraCache state table.
* Distinguish supported, not supported, not applicable, observation incomplete, configuration changed, proof expired, and not tested outcomes; every untested UI result includes its specific persisted reason.
* Migrate the legacy endpoint capability option into the existing UltraCache state record and remove the legacy option after successful persistence; no transient state is added.

= 2.59.12.42 =
* Make Automatic Varnish site flushing choose from the observed static-object route and verified production capabilities instead of preferring HTML-only flush unconditionally.
* Select verified entire-host flush when public static objects pass through Varnish, and select verified HTML-only flush when public static objects bypass Varnish.
* Report a verified HTML-only operation as degraded when static ownership is inconclusive or static objects pass through Varnish without verified entire-host invalidation.
* Display the actual site runtime strategy in the Varnish summary: HTML-only flush, entire-host flush, known site pages, or automatic expiry.
* Preserve the existing capability persistence, production transports, VCL contracts, and explicit manual scope behavior.

= 2.59.12.41 =
* Use one bounded post-invalidation observation contract for exact invalidation, batch BAN, HTML-only flush, entire-host flush, and static preservation proofs.
* Retry transient public DNS/HTTP observation failures while still requiring two successful responses that match the expected canary generation or exact written artifact.
* Fail immediately on any successful stale or mismatched observation so retries cannot manufacture a capability the production runtime does not support.
* Report exhausted transport-only observations as `observation-incomplete` instead of incorrectly classifying the server capability as unsupported.
* Preserve the existing production planner, control operations, capability tables, and persistent diagnostic contracts without adding transient state.

= 2.59.12.40 =
* Preserve the complete Varnish basic-test contract instead of truncating associative payloads by key count, recursion budget, insertion order, or string length.
* Persist the full ESI capability proof and endpoint/test evidence through the existing sanitization contract.
* Add an independent contract-bound HTML variant capability record and use it as the authoritative public-path registry source.
* Restore the HTML variant capability into the basic diagnostic response from its dedicated record and remove the redundant capability-list slice.
