VizProof Timeline

Description

VizProof Timeline connects WordPress to VizProof, a visual regression testing service, so every WordPress update can be followed by an automated visual check of your key pages.

Why

Plugin, theme, and core updates rarely throw PHP errors when they break something visual: a shifted button, a collapsed layout, a missing hero image. Your monitoring stays green while your pages degrade. VizProof Timeline catches those regressions right from wp-admin, before your visitors do.

How it works

  1. Click “Connecter à VizProof”: authorize the site from your VizProof account and the API token and project are configured automatically (a manual API token also works).
  2. Select the pages to monitor: each one is auto-classified as Single, Listing, or Static.
  3. Run updates as usual: a modal captures the “before” reference with live progress, the update then applies on its own, and a visual scan runs right after.
  4. Read the verdict first (OK / anomalies to review); pipeline and per-site details stay folded unless something is wrong.

Key features

  • One-click connect to VizProof, without any API token copy/paste (manual token still supported)
  • Settings profiles: “Simple (recommandé)” and “Prudent” presets configure the whole automation in one click; fine-grained settings live under “Réglages avancés”
  • Guaranteed before/after ordering: the “before” reference is captured and promoted before the update starts, from a progress modal, including for WordPress core updates
  • Optional strict mode: cancel the WordPress update if the baseline could not be captured in time
  • Post-update scans after plugin, theme, or core updates, including background automatic updates
  • Content-aware reference: publishing or editing content refreshes the baseline of the affected pages (partial re-capture on supporting servers), so editorial changes never show up as regressions
  • Verdict-first update panel and quiet, dismissible admin notices
  • Run history and visual diff summaries in wp-admin
  • Scheduled checks with WP-Cron (Action Scheduler fallback)
  • Plugin rollback with local backups when an update causes regressions
  • Multisite support in per-site and network-wide modes
  • WP-CLI commands (wp vizproof status|baseline|scan) with JSON output and CI-friendly exit codes, plus scans hooked into CLI-triggered updates

Requirements

A VizProof account and API token are required. The free plan is enough to get started: sign up at vizproof.com/register. See the Installation section for the step-by-step setup.

External services

This plugin connects to VizProof APIs hosted at https://vizproof.com (or a custom VizProof API URL configured by the site administrator).

It sends requests only when plugin features are used (project/page loading, scan launches, run history refresh, post-update workflows, scheduled scans).

Data that may be sent includes:

  • API token entered by an administrator
  • Linked VizProof site/project ID
  • Page IDs or URLs used for scans
  • Run trigger metadata required by update workflows

Service documentation and policies:

  • Privacy Policy: https://vizproof.com/privacy
  • Terms of Service: https://vizproof.com/terms

Installation

  1. Upload the vizproof-timeline folder to /wp-content/plugins/.
  2. Activate VizProof Timeline.
  3. Click VizProof in the top admin bar, then Configuration.
  4. Click Connecter à VizProof: sign in (or create a free account), authorize the site, and you are redirected back with the API token and project configured automatically.
  5. Select the pages to monitor and save.

Manual alternative (self-hosted servers or if you prefer explicit tokens):

  1. Create a free account at vizproof.com.
  2. Create a project and add at least one page (use your WordPress site URL).
  3. Go to Account API Tokens and generate a token (starts with vrt_).
  4. Paste the token in the plugin Configuration page and select the project.

Multisite:

  1. Open Network Admin -> Settings -> VizProof Timeline.
  2. Choose Per-site or Network-wide mode.

FAQ

Does this plugin require a VizProof account?

Yes. The free plan is enough to get started. The easiest path is the “Connecter à VizProof” button on the plugin Configuration page: it signs you in on vizproof.com and configures the token and project automatically. A manual API token (Account API Tokens) also works; see the Installation section.

Do I have to copy an API token?

Not anymore: one-click connect creates and configures a dedicated, revocable token for the site. Manual tokens remain supported (useful for self-hosted VizProof servers).

What happens after a plugin/theme/core update?

If post-update scan is enabled, VizProof Timeline queues a scan after the update completes and exposes status/results in wp-admin.

Does WP-CLI trigger the same scan logic?

Yes, in two ways. WP-CLI update commands run through the same updater hooks as an update started from wp-admin. And three dedicated commands are available once the site is connected:

  • wp vizproof status reports the connection, the selected project and the last scan.
  • wp vizproof baseline --wait captures a reference and promotes it, to run before a deployment.
  • wp vizproof scan --wait runs a scan and returns the visual verdict. Exit code 2 means anomalies were found, which lets a pipeline stop on a visual regression.

Add --format=json to any of them for a parsable output. The token and the project are read from the site configuration, so connect the site from wp-admin first.

Is multisite supported?

Yes.

  • Per-site: each subsite keeps its own configuration.
  • Network-wide: one shared network-level configuration.

Reviews

There are no reviews for this plugin.

Contributors & Developers

“VizProof Timeline” is open source software. The following people have contributed to this plugin.

Contributors

Translate “VizProof Timeline” into your language.

Interested in development?

Browse the code, check out the SVN repository, or subscribe to the development log by RSS.

Changelog

1.3.5

  • Fixed: nothing appeared below the update log to say the post-update scan had started. The result block was created by JavaScript on a page that writes itself progressively, so both its presence and its position depended on when the script happened to run. It is now rendered by PHP in the page footer, which is the one place guaranteed to come after the update log, and it says what it is waiting for even if the script never takes over.
  • Fixed: the admin bar spinner never started on the page running an update, which is exactly where you watch it. The scan is queued while that page is streaming, so the admin bar had already been rendered with an empty state and the script had nothing to poll. It now polls on that page regardless of the state it was handed.
  • Fixed: the running-scan line in the admin bar menu was not clickable. It now opens the results page.

1.3.4

  • New: the VizProof entry in the admin bar shows when a scan is running, on every admin screen. Its eye icon pulses, and the menu carries a live line with the current stage and how long it has been going. Until now a scan was only visible on the update screens and on the results page: anywhere else, nothing said one was in progress. The initial state is rendered with the page, so no request is made while nothing is running, and polling stops on its own after twenty minutes.

1.3.3

  • New: a post-update scan that finds nothing is promoted as the new baseline, on by default and switchable in the settings. Without it the reference ages at every accepted update, so differences already reviewed come back on the next scan. Promotion only happens on a completed scan with no failed run and no anomaly, so a regression is never frozen in as the new normal.
  • The scan card now says what was updated, with names and versions, instead of only “post-update scan”. The plugin already stored the detail and even had a function to format it, which was never called.
  • The history maintenance actions (clean up identical scans, mark as favourite, delete this batch) sit in a folded block. Given the same visual weight as “Accept and set as baseline”, they competed with it for attention without serving any daily purpose.

  • Fixed: a post-update scan took minutes to start, whatever the configured settle delay. That delay was applied by scheduling the job in the future, which is the one place it cannot work: nothing wakes a job dated later, so Action Scheduler’s async queue was bypassed and spawn_cron() had no event to trigger. The job waited for a cron pass instead. It is now queued immediately and the settle delay is held by the worker, between the cache purge and the capture, capped at 60 seconds so a sleeping process is never cut off. Retries keep their exponential backoff.

  • Fixed: the VizProof result appeared above the update log, so it read as if the scan had run before the update it comments on. The block is meant to sit below the update flow, but it was appended while the page was still streaming, which put it at the end of whatever had been written so far. It is now pushed back to the end on every render and once more when the page finishes loading.
  • Fixed: on the update execution page, the panel showed the result of a previous update under the “VizProof result after the update” heading whenever the current scan had not produced a run yet. The scan looked like it had run before the update. The listing page already guarded against stale results; the execution page now does too and shows the waiting state instead.
  • Fixed: the scan step list announced “all steps completed” while three steps were still pending. The summary only looked at running and failed steps, so a pipeline made entirely of pending steps read as finished.
  • The results screen shows when a post-update scan is queued or running, with how long ago it was requested and a link to the step-by-step view. Progress was only visible on the WordPress update screens, so the results page looked as if nothing had been started.

1.3.2

  • Cache purging before an update scan now covers WP Fastest Cache, Autoptimize, WP-Optimize, Cache Enabler, Hummingbird and Nginx Helper, on top of WP Rocket, LiteSpeed, W3 Total Cache, WP Super Cache, Breeze and SiteGround Optimizer. Plugins that combine and minify CSS into files whose URL changes on every purge were the ones missing: a page could be served pointing at a stylesheet that no longer existed, and the capture then showed the raw document, default list bullets included, which reads as a full-page regression.
  • Fixed: a run marked as favourite was displayed as the site baseline. The favourite badge reused the baseline badge’s CSS class, and the panel decided “this run is the baseline” by looking for that class in the page, so a favourited run had its verdict forced to green and was described as the site’s reference, hiding a failure. The two badges are now distinct, and the check reads the card’s actual state.
  • Fixed: the verdict opened with “No flow diffs detected” on scans that had found visual anomalies. That sentence is what the server returns when no flow ran at all, and the panel used it as a fallback when a run had no AI summary, so a message about flows announced a visual verdict. The flow summary now stays in the details, where its subject is clear.
  • Fixed: the post-update panel repeated itself. On a single site, the same sentence was rendered three times over, by the headline banner, the detail box and the per-site list. The banner keeps the verdict, the box keeps only what the banner has not already said, and the list is reduced to its status and its link. The “Results per site” heading no longer appears above a list of one.
  • The baseline bar says which scan is the reference, and takes you to it. When the reference is not among the loaded scans it said “Baseline active”, asserting that a reference exists without naming it, while no card below carried a badge. It now names the scan and its date, offers a “View this scan” button that selects and scrolls to its card, and says plainly when the reference sits outside the list.
  • The “Post-update scan” and “Baseline first” checkboxes no longer appear on the update execution page, where the choice has already been made and applied, and read as settings for what just happened. They stay in the page so the panel keeps polling for the scan result, but they are no longer presented as controls.
  • Fixed: “Baseline status unavailable.” was the last English string left in the French interface.

1.3.1

  • Fixed: page titles containing an apostrophe showed up as “Page d'accueil” across the results screen. A WordPress title can be stored with HTML entities depending on the editor that created it; sent as-is when the page was registered, the entity was frozen into the VizProof record and escaped a second time on display. Titles are now normalized before they leave WordPress, and what comes back from the API is cleaned too, so records created before this release also display correctly.
  • The results screen had accumulated around thirty tinted surfaces, including six different pale blues for plain containers, which left no visual hierarchy at all. Colour is now reserved for what carries meaning: the scan verdict, and the active baseline. Everything structural sits on two neutral surfaces and two neutral borders, driven by a small set of CSS variables. The palette went from 180 hard-coded colours to 144.
  • The category chips (SEO, structure, tone) no longer each carry their own colour, and the “Action” text is no longer green: their label already says what they are.
  • The results screen is split in two titled sections. Scan results used to render under the “Historique des scans” heading, with no section or title of their own: the capture summary landed at the very top of the page, far from what it summarised, and the simple/expert switch floated between the scan list and the verdict, anchored to neither. Results now have their own section, whose header groups the title, the summary and the switch. The page navigation no longer sits between the two.

1.3.0

  • New: three WP-CLI commands, wp vizproof status, wp vizproof baseline and wp vizproof scan. They drive the same pipeline as the admin screens, take --wait, --timeout and --format=json, and return an exit code a build can act on: 0 when clean, 2 when the scan found visual anomalies, 1 on a technical failure. The site still has to be connected from wp-admin first, as the commands read the stored token and project.
  • Fixed: the reference capture taken before an update failed with “Erreur VizProof 404 sur POST /api/pages/75/capture” whenever the scan is limited to selected pages. Three call sites handed WordPress post IDs to the scan launcher as an explicit page list, and the launcher only translates page IDs when it receives none, so raw WordPress IDs reached the capture endpoint. The translation now happens in one shared place, on the target site in multisite so the permalinks resolve against the right blog.
  • Fixed: apostrophes showed up as ' in the update dialog and the update panel. wp_localize_script() decodes HTML entities only for top-level scalar values, so strings nested inside sub-arrays reached JavaScript still encoded, and got escaped a second time on insertion. The whole payload is decoded before it is handed over.
  • The WP-CLI class is only loaded when running under WP-CLI, instead of on every web request.

1.2.5

  • Fixed at the root: one-click connect linked the project but never saved the API token. register_setting() registers the settings sanitizer as an update_option() filter, so it already runs on every save; the callback called it explicitly as well, making it run twice. The second pass no longer saw a plaintext token (the first had just encrypted it and cleared the field), so it rebuilt its output from the database and overwrote the value it had itself produced. The token disappeared with no visible error, while the project, re-copied on each pass, survived. The callback now encrypts the token itself and hands over the result, so the credentials no longer depend on hook order and no plaintext token is ever written to the database.
  • The sanitizer is idempotent from now on: an already-encrypted token passed as input is preserved, so re-sanitizing its own output no longer loses credentials.
  • The token returned by the server is checked against the expected format before being stored, and an encryption failure now reports the reason instead of a generic message.
  • Fixed: the plugin page header rendered unstyled, as a bare icon and title taking excessive height. Its CSS was injected with wp_add_inline_style() from the page body, long after WordPress had printed the stylesheets in <head>, so it never reached the page. The rules now live in the plugin stylesheet.
  • Fixed the same trap at its source for every late stylesheet: a handle stays flagged as enqueued once printed, so the plugin kept attaching CSS to a finished handle. It now falls back to a virtual handle that print_late_styles() outputs in the footer, which also restores the timeline page styles.
  • Scans queued asynchronously are now cancelled when the plugin is deactivated. They used to stay in the Action Scheduler queue with no handler left to run them, and piled up as failed actions.
  • Translation template and French catalog regenerated: around 200 strings added since 1.0.0 were missing from them.

1.2.4

  • Fixed: one-click connect reported success while the API token was silently dropped (the project was linked, but the Configuration screen still asked for a token). The callback reused the settings-form sanitizer, which is built for form input and reports problems through add_settings_error(), messages that are lost across the redirect that follows. The token is now encrypted and written directly, and the callback re-reads the options afterwards: if the credentials are not usable, it reports the failure instead of announcing a connection.

1.2.3

  • Fixed: the one-click connect link was built with add_query_arg(), which does not URL-encode values. The callback URL contains ?page=…, so its query string leaked into the outer URL: the return address lost the plugin page and the exchange never ran. Values are now encoded with http_build_query().

1.2.2

  • Fixed: with no API token set, opening the Results or API Logs screen returned “Sorry, you are not allowed to access this page”. Hiding those entries in 1.2.1 also unregistered the pages from WordPress, which turned a hidden menu item into a hard permission error on the direct URL. The pages are registered again and simply redirect to Configuration while the site is not connected.

1.2.1

Admin interface cleanup, on every plugin screen:

  • The page header (shared by Configuration, Results, API Logs, Pages and the network screens) overflowed to the right: .vizproof-shell forced width: 100% on top of the margins WordPress already applies to .wrap. Width removed, max width set instead.
  • That header was also defined twice, in the stylesheet and inline. The old gradient banner, its grid pattern, blurred halo and three decorative bars are gone, along with a stray rule that rendered the title at 48px on mobile. What remains is one sober header: white card, red accent, product mark, title and description.
  • One-click connect is now offered wherever the site is not linked yet, not only on the Configuration screen.
  • Until an API token is set, only the Configuration entry is shown: the Results and API Logs screens, and their admin bar entries, stay hidden since they would have nothing to display.

1.2.0

Reliable before/after ordering, including WordPress core updates:

  • The “before” baseline is now captured from a dedicated modal, before you reach the update screen. WordPress core updates never fired upgrader_pre_install, so the baseline used to start at the same time as the update and was unusable; the browser-side guard fixes this for core, plugins and themes alike.
  • The modal shows real progress (captured / expected screenshots) and lets you cancel, or update without a reference, at any time. No more frozen-looking update screen.
  • Once the reference is promoted, the update resumes automatically and the post-update scan still runs.
  • New REST endpoints POST|GET /vizproof/v1/pre-update-baseline to start and follow the capture without blocking any PHP request.
  • Settings redesign: the recommended profiles are now real selectable cards (they were rendered as plain full-width buttons, since WordPress admin button styles overrode the layout), section headers are readable, “Réglages avancés” is a proper disclosure, and setting rows get separators and legible descriptions.
  • Admin bar: the VizProof entry is now clickable (it opens the scan results), carries an eye icon so it stands out, and its submenu labels are explicit (“Résultats des scans”, “Réglages du réseau”).

1.1.1

  • Copy editing pass across the plugin UI, notices, and readme: clearer phrasing and punctuation cleanup, no functional change.

1.1.0

Simplicity release: one-click setup, guided settings, live progress.

  • One-click connect: a “Connecter à VizProof” button opens vizproof.com where you authorize the site; the API token and project are created and configured automatically (single-use code exchange, no copy/paste). Requires a VizProof server advertising the wordpressConnect capability; the manual token path remains available.
  • Settings profiles: “Simple (recommandé)” and “Prudent” presets fill the whole automation form in one click; all fine-grained settings now live in a folded “Réglages avancés” section.
  • Live progress during the pre-update pause: the update screen streams “capture en cours… x/y captures” lines while WordPress waits for the “before” baseline, instead of appearing frozen.
  • Verdict-first update panel: after a scan the big binary verdict (OK / anomalies) is the hero; pipeline and per-site details are folded into “Détails techniques”, auto-opened only when something is wrong.
  • Fixed a PHP 8.2+ dynamic-property deprecation on the plugin bootstrap class.
  • Updated “Tested up to” to WordPress 7.1.

1.0.4

Pre-update baseline ordering and quieter notices:

  • The WordPress update now waits for the “before” baseline capture to finish (and be promoted) before any file is replaced, instead of giving up after ~60 seconds. The wait budget is configurable (30–600 s, default 240 s) and the PHP execution limit is refreshed during the wait.
  • New strict mode “Require pre-update baseline”: if the baseline cannot be captured and promoted within the budget, the WordPress update is cancelled before touching any file (opt-in, off by default).
  • The update panel announces the pause (“Baseline « avant » en cours de capture…”) and a new “Baseline avant mise à jour” step appears in the scan pipeline with its outcome (done / failed / skipped).
  • When strict mode cancels an update, the panel explains why instead of pretending a post-update scan ran.
  • Admin notices are quieter: compact styling, softer tone (regressions are now a warning, red is reserved for hard scan failures), 24-hour window instead of 48, and a persistent “Ne plus afficher” dismissal per notice.
  • The activation welcome notice only appears on the Dashboard, Plugins and Updates screens, and can be permanently dismissed.
  • Page classification: each tracked page is auto-classified as Single / Listing / Static (blog index detected as Listing), with a manual override select in the page selector for custom listing pages.
  • New “Re-baseline after content change” automation (opt-in): publishing, editing or unpublishing content re-captures the tracked pages and promotes the run as the new reference, so editorial changes stop showing up as visual regressions. Debounced (configurable quiet delay, default 10 min) so a burst of edits triggers a single scan; it only fires when a tracked page is affected (the content’s own tracked page, or a tracked Listing page).
  • Content-rebase runs never raise “visual regression” admin notices (their diffs are the expected content change); hard failures are still notified.
  • New option to also capture the pre-update baseline during background automatic updates (WP-Cron), enabling safe unattended auto-updates.
  • Partial rebase: on VizProof servers advertising the pageBaseline capability, a content change only re-captures the affected pages (edited single + tracked listings) and pins them as their own baseline via POST /api/sites/{id}/baseline-pages; the site baseline and every other page stay untouched. Falls back to the full rebase on older servers.
  • Server capability discovery now probes /api/capabilities (with /capabilities fallback), so feature detection works behind reverse proxies that only forward /api/*.

1.0.3

UX and reliability improvements based on real-world testing:

  • Update panel now shows live scan progress: pipeline steps stay expanded while a scan is active, with an elapsed-time counter refreshed every second.
  • Clearer status copy during post-update scans (queued vs running phases are now distinct and accurate).
  • Stuck-scan detection: the UI warns when a queued scan has not started after 4 minutes or a running scan exceeds 20 minutes, with actionable hints (WP-Cron / Action Scheduler, API logs).
  • Server-side stale-scan sweep: scans stuck in “queued” for 30+ minutes (or “running” for 2+ hours) are automatically finalized as failed instead of polling forever.
  • Fixed error messages showing raw JSON instead of the human-readable message returned by the plugin.
  • Dedicated messages for invalid/expired tokens (401/403) and plan quota reached (429); upstream 429 is no longer retried 4 times.
  • Token validity is now verified against the VizProof API (cached 5 min) and surfaced on the dashboard Token card with an inline error, instead of showing “OK” for any non-empty string.
  • Results page: the “Launch scan” button is disabled until the plugin is configured, and the notice now links to the Configuration page.
  • Welcome notice after activation linking to the Configuration page (dismissed automatically once configured, expires after 14 days).
  • Fixed a JavaScript syntax error (unescaped apostrophe) that silently disabled the onboarding health-check module.

1.0.2

  • Updated “Tested up to” to WordPress 7.0.
  • Removed the duplicate README.md from the distributed package.

1.0.1

  • Fixed readme: corrected menu location from “Settings > VizProof Timeline” to the actual top-level “VizProof” admin bar menu.
  • Added step-by-step instructions for obtaining a VizProof API token in the Installation section.
  • Fixed admin notices from other plugins appearing inside VizProof pages (added screen-reader-text h1 anchor for WordPress notice injection).
  • Redesigned timeline baseline status bar into a unified single-row component.
  • Removed unused baseline-assist and baseline-summary CSS classes.

1.0.0

First stable WordPress.org release. The plugin has reached production maturity for the VizProof Timeline integration: multisite-aware updates workflow, asynchronous scan queue with retry/backoff, admin bar regression badge, REST API surface, and rollback service with local backups in the uploads directory.

WordPress.org review compliance:
* Removed direct loading of wp-admin/includes/misc.php (no function from that file was used) and gated remaining require_once ABSPATH . 'wp-admin/includes/...' calls with function_exists() / class_exists() guards. Each guarded include is followed immediately by a call to a function/class from that file, per WP.org guidelines.
* Replaced hardcoded WP_PLUGIN_DIR and WP_CONTENT_DIR constants in the rollback service with paths derived from plugin_dir_path( __FILE__ ) via new VIZPROOF_TIMELINE_PLUGIN_FILE / VIZPROOF_TIMELINE_PLUGIN_DIR / VIZPROOF_TIMELINE_PLUGIN_URL constants. Local backup path validation now reuses the existing wp_upload_dir()-based get_local_backup_root() helper for consistency.
* Made REST permission callbacks (can_access_timeline, can_launch_runs) perform an explicit current_user_can( 'manage_options' ) capability check (previously delegated only via can_manage_plugin()), so static analysis can confirm protection on all 23 endpoints.
* Hardened enqueue_inline_admin_style() / enqueue_inline_admin_script(): CSS now goes through wp_strip_all_tags() before wp_add_inline_style(), and inline JS is rejected if it contains a </script> sequence that could break out of the inline <script> block.
* Renamed admin bar regression transients from vizproof_adminbar_regressions_* to vizproof_timeline_adminbar_regressions_* for prefix consistency with the rest of the plugin.

0.4.45

WordPress.org compliance:
* Replaced admin inline <style>/<script> blocks with wp_add_inline_style() / wp_add_inline_script() attached to enqueued handles.
* Moved admin bar badge CSS injection to admin_enqueue_scripts.
* Update panel assets now load consistently on update-core.php, themes.php, and plugins.php.
* Added dedicated nonce handling for post-update request flags (vizproof_after_update_scan, vizproof_before_update_baseline) and enforced nonce+capability checks before honoring explicit request overrides.

0.4.44

Bug fixes:
* Fixed post-update scan failing silently when pages already exist on VizProof: the existing page ID from 409 responses was not extracted (nested in proxy body, not at details root).
* Pre-update baseline promotion now polls until the VizProof run completes (up to ~60s) before promoting, instead of promoting an incomplete run which was always rejected by the API.

Improvements:
* API logs now display in chronological order by default (oldestnewest) with a toggle button to switch to reverse order.
* Log rotation: entries older than 7 days are automatically pruned. Retention increased from 120 to 500 entries.
* Log rows with 4xx errors are highlighted in yellow, 5xx in red.

0.4.43

  • VizProof update panel now displays on Themes and Plugins admin pages (was limited to update-core.php).
  • Assets (CSS/JS) are now loaded on themes.php and plugins.php for update panel rendering.

0.4.42

Security:
* Fixed path traversal in restore_local_plugin_backup: added trailing slash to realpath containment check.
* Added bearer token redaction in proxy error log bodies.
* REST rest_save_option now runs options through sanitize_options before update_option.

WordPress compliance:
* Added load_plugin_textdomain() on init for translation loading outside WP.org directory.
* Capped get_sites() calls to 500 (was unbounded number=0, caused timeout on large Multisite).
* Plugin header Description translated to English (WP.org requirement).
* Merged duplicate REST route registrations for runs/{id} (GET+DELETE in single call).
* Complete uninstall cleanup: removes vizproof_lock* options, all vizproof_* transients and site transients.

Accessibility (WCAG 2.1 AA):
* Page tree checkboxes now include page title in aria-label (“Suivre : Page title”).
* Added label/for association on network settings cache delay input.
* Removed role=”presentation” from per-site targets data table.
* Fixed color contrast: warn badge (#6b5100), pending pill (#7a5300).
* Added :focus-visible styles on all custom buttons and clickable elements.
* Replaced invalid

<

h3> inside

<

legend> with styled

<

legend> in automation fieldsets.

i18n:
* Translated network config mode radio labels to French (was mixed EN/FR).
* Wired ~10 hardcoded strings in inline JS bridge to t() translation helper.
* Translated 8 untranslated msgstr entries in fr_FR.po (update summary labels, Status, etc.).

Cleanup:
* Removed dead SETTINGS_MENU_SLUG constant and render_settings_page() method.
* Removed ~6 unused CSS selectors (activity-item, summary-list, health-check).
* Capped scan history to 100 entries (was unbounded).
* Added TODO comments on duplicated inline blocks for future consolidation.

0.4.41

  • Fixed proxy_vizproof_request silently failing when receiving pre-normalization options with empty api_token (self-decrypt from api_token_encrypted).
  • Fixed scan lock race condition on standard WP installs (no Redis): replaced non-atomic transient fallback with INSERT IGNORE for cross-process atomicity.
  • Removed orphan SETTINGS_MENU_SLUG from is_vizproof_admin_request allowlists (page never registered in admin_menu).
  • Removed dead code: register_admin_menu, register_network_admin_menu, register_settings from service-base (duplicated in main plugin file, never hooked).

0.4.40

  • Fixed PHP parse error caused by Unicode smart quotes in French translation strings.
  • Fixed dead links: “Réglages avancés” and “Choisir le projet” pointed to unregistered SETTINGS_MENU_SLUG page.
  • Simplified redundant boolean comparisons (=== true / === false) in update panel JS.
  • Further compacted update panel CSS: tighter padding, margins, and gaps across all panel sections.

0.4.39

  • Fixed redundant boolean logic in token storage validation.
  • Fixed race condition in scan lock using atomic wp_cache_add.
  • Fixed REST route methods for favorite endpoints (PATCH only instead of PUT+PATCH).
  • Fixed function_exists guard in uninstall.php referencing wrong function name.
  • Simplified site picker: merged auto-detect into project loading, “Create from WordPress” is now a secondary link.
  • Added AJAX save for project selection (no full page reload between onboarding steps).
  • Grouped automation settings into sub-sections: Planification, Comportement post-update, Notifications.
  • Moved API logs behind a collapsible “Outils développeur” toggle on the dashboard.
  • Added empty state on the timeline when no runs exist.
  • Translated raw API status strings (partial_failed, queued, etc.) to human-readable French in the update panel.
  • Replaced setInterval polling with MutationObserver only in the timeline bridge script.
  • Added aria-live attributes on all dynamically updated zones (state, runs, diffs, update panel).
  • Fixed timeline filter labels: proper label-for associations instead of span wrappers.
  • Restructured network settings form with fieldset groups (Connexion API, Comportement des scans, Notifications).
  • Compacted update panel on update-core.php: single-row header, inline checkboxes, collapsible pipeline steps.
  • Stale scan results from previous updates are no longer shown on the update listing page.

0.4.38

  • Improved update-flow reliability for async and sync scan events.
  • Improved multisite admin URL routing between site and network contexts.
  • Improved pre-update baseline behavior by promoting successful pre-update runs.
  • Improved update panel consistency for partial-failure handling.
  • Improved layout width handling on update-core screens.

0.4.0

  • Added asynchronous queue support with retry/backoff.
  • Added update workflows and visual regression notifications in wp-admin.
  • Added multisite support for per-site and network-wide modes.