30 days free. No credit card. Full access from the moment you connect your site.

Start free trial

v0.9.8 — Hotfix: abilities-manager fatal guard + 2 MailerPress-upstream drift shims

Three fixes shipped across two PRs:

  • #72 (Fix 1) — activation-time fatal guard when acrossai-abilities-manager sibling plugin is inactive.
  • #73 (Fix 2 + 3) — mailerpress-pro/list-webhooks response-shape normalization + mailerpress/update-campaign read-modify-write shim.

Fix 1 — Activation-time fatal guard

Sites running acrossai-pro 0.9.5+ WITHOUT the sibling acrossai-abilities-manager also active would fatal on plugins_loaded @ 20:

PHP Fatal error: Uncaught Error: Class "AcrossAI_Abilities_Manager\Includes\Modules\Library\Ability_Definition"
  not found in .../includes/Abilities/MailerPress/Contacts/List_Contacts.php:21

Root cause. AcrossAI_Pro_Abilities_Bootstrap::register_abilities() iterated its Integration_Bootstrap registry and instantiated ability classes regardless of whether the sibling plugin was active. Instantiation triggered the Jetpack autoloader → loaded the ability file → fatal on its extends Ability_Definition clause.

Fix. Static has_abilities_manager() probe that checks class_exists('\AcrossAI_Abilities_Manager\Includes\Modules\Library\Ability_Definition'). Both register_abilities() and register_category_callbacks() early-return on its negation. Plugin now activates cleanly and no-ops its ability suite when the sibling is absent — per DEC-DEPENDENCY-NOTICE-NOT-DIE (D12).

Critical invariant: leading backslash on the probe FQN. Without it class_exists resolves namespace-relative and always returns false — the plugin would silently register zero abilities even when the sibling IS active. Regression test explicitly locks this in.

Fix 2 — mailerpress-pro/list-webhooks response-shape drift

MailerPress-side inconsistency: empty state returns [] (bare array), populated state returns {webhook_id: {...}, ...} (associative map keyed by id). Callers iterating the payload write buggy code that works with 0 or 2+ webhooks but crashes with exactly 1 (or vice versa depending on which case they test first).

Fix. Ability normalizes both shapes to {webhooks: [{id, secret, enabled, description}, ...]} — empty → {webhooks: []}; associative map → extract via array_values().

Sixth documented instance of BUG-ENDPOINT-DRIFT-PATTERNS (B15) with a new sub-variant flagged: shape switches with cardinality.

Fix 3 — mailerpress/update-campaign read-modify-write shim

Two MailerPress-upstream bugs in Campaigns::edit() (mailerpress/src/Api/Campaigns.php ~lines 2178-2180):

  • BUG-UPSTREAM-CAMPAIGN-DOUBLE-ENCODE: 'content_html' => !empty($meta['json']) ? wp_json_encode($meta['json']) : wp_json_encode($campaign->content_html) — the fallback re-encodes an already-encoded JSON string. Every PUT that omits meta.json nests the encoding one level deeper; after N such PUTs the stored content_html is JSON-encoded N+1 times and the block editor can't parse it.
  • BUG-UPSTREAM-CAMPAIGN-CONFIG-WHOLESALE-REPLACE: 'config' => !empty($meta['emailConfig']) ? wp_json_encode($meta['emailConfig']) : $campaign->config — a partial meta.emailConfig payload wholesale-replaces the stored config, wiping campaignName / editorType / sendChoice / sendAt / lists / campaignList (any key not in the partial).

Shim design. Update_Campaign::execute() now:

  1. GET /campaign/{id} → returns decoded json + config fields
  2. Decode via response-shape keys (json for content, config for emailConfig — NOT the DB-side keys)
  3. Deep-merge caller partials via array_replace_recursive
  4. Send FULL merged payload back — MailerPress's non-empty branches trigger correctly (single-encode + full-config replace = intact merge since caller values overlay current)
  5. If GET fails: return update_precondition_lookup_failed and refuse the PUT

Both upstream bugs will be reported to MailerPress separately. Shim is idempotent + correct even after upstream fixes land, so no future removal required for correctness.

Test coverage

454 tests / 1657 assertions (0.9.7) → 457 tests / 1673 assertions (0.9.8) — three new source-content regression tests:

  • test_bootstrap_guards_register_abilities_on_abilities_manager_presence
  • test_update_campaign_shims_upstream_bugs_via_read_modify_write
  • test_list_webhooks_normalizes_upstream_cardinality_drift

Zero effect on happy-path installs

Sites with both plugins active + callers sending full campaign meta payloads see no behavioural change. The three fixes only trip when the failure mode they address would have triggered — otherwise they're pure-passthrough with one extra class_exists / GET /campaign/{id} / array_values call respectively.

Merged PRs

  • #72 — hotfix(0.9.8): guard AcrossAI_Pro_Abilities_Bootstrap on sibling plugin
  • #73 — fix(abilities): 2 MailerPress-upstream drift shims (sibling of #72 for 0.9.8)

Previous release

For v0.9.7 (Feature 017: MailerPress workflow abilities), see v0.9.7 release notes.


Keep reading