Skip to content

Release - #1379

Merged
selul merged 20 commits into
masterfrom
development
Sep 30, 2026
Merged

Release#1379
selul merged 20 commits into
masterfrom
development

Conversation

@pirate-bot

@pirate-bot pirate-bot commented Sep 12, 2026 •

Copy link
Copy Markdown
Contributor

Linked issues

This release will close the following issues once merged:

Public changelog

  • Fixed scheduled chart updates crashing on WordPress versions before 5.5.
  • Fixed the Visualizer readme with the current version and recent changelog entries.
  • Added AI agent support: let AI assistants read and change your Visualizer charts and settings.
  • Fixed database chart refreshes stopping after an interrupted run.
  • Improved Visualizer guidance for finding scheduled database refresh actions.
  • Updated dependencies

pirate-bot and others added 3 commits September 11, 2026 21:57
* fix: guard action scheduler

* fix: update PHP version

* fix: enhance action scheduler checks

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>

---------

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: pirate-bot <58979018+pirate-bot@users.noreply.github.com>
@pirate-bot

Copy link
Copy Markdown
Contributor Author

Make sure you've reviewed the themeisle_sdk_compatibilities filter of the plugin and updated it accordingly.

@pirate-bot

pirate-bot commented Sep 12, 2026 •

Copy link
Copy Markdown
Contributor Author

Plugin build for 93eb525 is ready 🛎️!

Copilot AI and others added 17 commits September 14, 2026 19:55
Co-authored-by: HardeepAsrani <2649903+HardeepAsrani@users.noreply.github.com>
…me-tag

Align readme stable tag with release version and trim changelog surface
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018GJfggt4S4RuAfF24E93EK
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018GJfggt4S4RuAfF24E93EK
Bumps [codeinwp/themeisle-sdk](https://github.com/Codeinwp/themeisle-sdk) from 3.3.61 to 3.3.62.
- [Release notes](https://github.com/Codeinwp/themeisle-sdk/releases)
- [Changelog](https://github.com/Codeinwp/themeisle-sdk/blob/v3.3.62/CHANGELOG.md)
- [Commits](Codeinwp/themeisle-sdk@v3.3.61...v3.3.62)

---
updated-dependencies:
- dependency-name: codeinwp/themeisle-sdk
  dependency-version: 3.3.62
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018GJfggt4S4RuAfF24E93EK
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018GJfggt4S4RuAfF24E93EK
Bumps [phpstan/phpstan](https://github.com/phpstan/phpstan-phar-composer-source) from 2.2.13 to 2.2.14.
- [Commits](https://github.com/phpstan/phpstan-phar-composer-source/commits)

---
updated-dependencies:
- dependency-name: phpstan/phpstan
  dependency-version: 2.2.14
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
The first SDK release that carries the AI Connect module this PR opts into.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018GJfggt4S4RuAfF24E93EK
Picks up the AI Connect fixes since 3.3.64: the Enable button hides once
the connector is active, the notice matches core's height, and the
"enabled" event for products with their own notification UI.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018GJfggt4S4RuAfF24E93EK
feat: register abilities with the Abilities API
- Added AI agent support: let AI assistants read and change your Visualizer charts and settings.
* fix: keep the DB refresh scheduled when Action Scheduler creation fails

as_schedule_recurring_action() returns 0 instead of throwing when it cannot
store an action. The activation path ignored that return, cleared the WP-Cron
fallback anyway, and left the refresh hook with no trigger at all. Nothing
recovered it: the recovery hook stood down whenever Action Scheduler was
usable, and the migration was gated on the WP-Cron event that had just been
deleted, so even a reactivation could fail the same way.

Clear the fallback only after re-querying Action Scheduler for the action.
Turn the recovery hook into a plain "is anything going to fire this hook"
check, which also covers the legacy WP-Cron migration, so the duplicate
scheduling code in Visualizer_Module_Upgrade is no longer needed.

Two consequences of running that check on every request:

- Re-arm WP-Cron only when the event is missing or its interval changed.
  Re-arming unconditionally pinned it to a past timestamp on every request,
  so the refresh became due on every cron spawn.
- Schedule the action as unique. Action Scheduler creates the next recurrence
  only after the current one completes, so a concurrent request can arrive
  while nothing is pending and add a duplicate.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* fix: clear a WP-Cron event left beside the Action Scheduler action

Both schedulers fire visualizer_schedule_refresh_db, so a site holding an
action and an event refreshes twice per interval. The recovery check counted
the action alone as settled and left the event in place. Treat the refresh as
settled only when Action Scheduler holds the action and no event fires the
same hook beside it.

Also from review: reuse the first lookup instead of querying Action Scheduler
again when the action already exists, and name the hook and the group once
instead of repeating the literals.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* test: skip instead of fatal when Action Scheduler is not loaded

index.php loads Action Scheduler only when visualizer_can_use_action_scheduler()
passes, so a host without wpdb::db_server_info() has none. set_up() called the
library unconditionally, which ended the whole suite with a fatal before any
test could report. Skip the file instead.

Also corrects the WP-Cron comment: the check is missing or different interval,
not a stale timestamp. A timestamp criterion would pin the event to the past
again, which is what the check exists to prevent.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* fix: recover the refresh chain a killed run ends, and throttle the check

Hands-on investigation on the reporting site corrected the premise of #1384.
Reactivation did restore the action; the customer was reading WP Crontrol,
where the hook correctly no longer appears after the Action Scheduler
migration. The real defect is that a run killed mid flight ends the recurring
chain for good: Action Scheduler creates the next occurrence inside
schedule_next_instance(), which a host kill, fatal or timeout never reaches,
and the queue cleaner then marks the action failed with no successor.

The per-request check already recovers this. Add the two pieces it was missing:

- Hook action_scheduler_ensure_recurring_actions, Action Scheduler's own daily
  assurance hook, as a floor under the per-request check for sites that serve
  few requests.
- Throttle the per-request check to one run per 300s, recorded in an autoloaded
  option so a settled site spends no query on it. Action Scheduler needs the
  same 300s to mark a killed run failed, so a shorter window cannot recover one
  any sooner. The daily hook ignores the window.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* fix: start a recovered run now, and stop rewriting an autoloaded option

Two findings from review.

The start time is local midnight derived from gmt_offset. West of UTC that
midnight has not arrived yet, so a recovered run was parked up to twelve hours
ahead and the charts stayed stale for the rest of the day. Fall back to the
previous midnight when the computed one is still in the future.

The throttle recorded its timestamp in an autoloaded option, so every window
rewrote the alloptions blob and invalidated it for every process. A transient
with the window as its expiry is not autoloaded, expires on its own, and keeps
the read cached.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* test: prove the filter argument order instead of assuming it

Review has now questioned the pre_as_schedule_recurring_action argument order
twice. The racer records the two arguments it receives and the test asserts
their types, so a swap fails loudly instead of silently forwarding the wrong
value. Types separate them whatever value the code under test passes.

Also from review: guard set_up() on the ActionScheduler classes the tests call
statics on, not only the functions, and scope the gmt_offset change to a filter
the framework restores.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* test: do not depend on plugin_basename() for the lifecycle hook name

plugin_basename() resolves differently depending on where the plugin directory
is loaded from, so the hook name register_activation_hook() used is not stable
across environments. Loading the plugin from outside WP_PLUGIN_DIR registers
the full path as the hook name while the test computes the short one, and the
activation test then fires a hook nothing listens to and passes vacuously or
fails. Call the callback the hook points at instead.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* test: say why the Action Scheduler precondition fails instead of skipping

A red build on this assertion needs to point somewhere. Absent is skipped in
set_up() because some hosts cannot run Action Scheduler; loaded but not
initialized means the load order broke, and every other test in the class would
then assert nothing.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* fix: do not drop the refresh on an unknown interval or a fractional offset

Two findings from review, both on the WP-Cron fallback.

wp_schedule_event() refuses a schedule WP-Cron does not know, and the fallback
cleared the existing event before asking, so a visualizer_chart_schedule_interval
filter returning an unregistered key left the refresh with nothing and the
throttle then held the retry off. Fall back to the plugin's own schedule when
the filtered key is not registered, which is what get_schedule_interval_seconds()
already does for the interval itself.

gmt_offset is a number rather than an integer, so the start time could carry a
fraction. WP-Cron keys its array by that value, and PHP reports losing precision
from wp-includes/cron.php. Cast the start time to an integer.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* docs: note that tear_down restores hooks, so test filters need no removal

Reviewers have read the missing remove_filter() calls as a leak four times
across this PR and its pro counterpart. WP_UnitTestCase_Base backs up
$wp_filter in set_up() and restores it wholesale in tear_down().

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* fix: record the check window only when a trigger exists

The window skips work that is already done, so recording it after an attempt
that established no trigger left the site without one until the window expired,
with the daily assurance hook a day away. ensure_refresh_db_action() now reports
whether the refresh ended up scheduled, and the caller caches only that.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* fix: treat a live WP-Cron fallback as scheduled for the check window

Where Action Scheduler is present but refuses, the fallback is the trigger, and
the previous commit reported that as unscheduled. The window was then never
recorded, so every request repeated the whole check and the refused insert.

Split the two questions: settled means the refresh sits on the scheduler this
site should use, which is what decides whether to act; having a trigger means
something will fire the hook at all, which is what decides whether to cache.
ensure_refresh_db_action() goes back to returning nothing.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* refactor: read the cron schedules once when scheduling the refresh

The key check and the interval lookup each called wp_get_schedules(). Read it
once and derive both from the same array, which also makes the one-line helper
that did the second lookup unnecessary.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* fix: keep the old WP-Cron event until its replacement is scheduled

Changing interval cleared the old event and then scheduled the new one, so a
refused schedule left the refresh with nothing. That is the bug this PR fixes on
the Action Scheduler path, repeated on the WP-Cron one.

Schedule first and remove the old event by its own timestamp afterwards.
wp_clear_scheduled_hook() would remove the new event too, since WP-Cron does not
dedupe recurring events for a hook. A matching timestamp means the write already
replaced the old entry in place, so nothing is removed in that case.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* fix: unschedule the old refresh event without passing its arguments

The refresh is scheduled without arguments, so the old event has none to match.
Keeps this in step with the Pro plugin, where PHPStan objected to passing the
typed-as-array field where a list is expected.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* docs: trim the comments

Requested in review: keep the one line that says why, drop the paragraphs.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* test: type the pending action IDs as numeric strings

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
@selul
selul merged commit 3c987b6 into master Sep 30, 2026
8 of 9 checks passed
@pirate-bot

Copy link
Copy Markdown
Contributor Author

🎉 This PR is included in version 4.0.9 🎉

The release is available on GitHub release

Your semantic-release bot 📦🚀

@pirate-bot pirate-bot added the released Indicate that an issue has been resolved and released in a particular version of the product. label Sep 30, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

released Indicate that an issue has been resolved and released in a particular version of the product.

Projects

None yet

6 participants