v2.3.0 — API contract safety
API contract note
Additive only. No existing behaviour changes for a site that installs this release. Every new safety mechanism is opt-in on both sides: a backend without the handshake or /meta endpoint behaves exactly as before, and a site running an older plugin is untouched by a backend that has them.
Why this release exists
A tenant asked for API versioning guarantees so that our backend deploys could not break their live site. Investigating that surfaced something bigger: every version before this one cleared the local tables and then fetched. If the API failed at that moment, the site went blank. That is fixed, along with three other paths that could empty a site.
What it does
Contract handshake. Every API request sends X-DataFlair-Plugin-Version, plus X-DataFlair-Expected-Contract on versioned endpoints. A backend that cannot serve the expected contract answers HTTP 409; sync pauses with a clear admin notice and the site keeps serving its last good data. State is per sync stream, so toplists and brands cannot mask each other.
Contract canary. Sync payloads are deep-validated on every page before any local write. A renamed, removed, or retyped render-critical field (offer, offerText, brand linkage, trackerLink, geo and the code / coveredCountries the geo gate matches on) stops the sync and names the field. Collective all-or-nothing checks with a minimum sample make false positives on legitimate partial data impossible; present-but-null keys are always valid.
Fail-safe sync ordering. The destructive stale-row wipe runs only after a response is fetched and validated. An empty payload against a populated site refuses the wipe. Low-budget requests skip the wipe and upsert rather than blanking the table.
Drift-resilient rendering. Card templates, ProsConsResolver and the shortcode decode boundary survive hostile retypes with zero notices under WP_DEBUG_DISPLAY.
API version awareness. Each full sync reads the backend's /api/vN/meta and raises a dismissible informational notice when the contract revision moves or a newer API version becomes available. The first reading is a silent baseline.
Declared database contract. The plugin tables are locked by a test: additive only within a major version, with the data column storing the verbatim API payload, for sites that read the tables directly instead of using the shortcode.
wp dataflair sync. The Settings page had long offered WP-CLI as a way to trigger a sync, but no such command existed. It does now, with --only=toplists|brands, a non-zero exit so a real system cron can react, and automatic backoff on API rate limits. There is still no WP-Cron, by design.
Visibility. An API Contract Check diagnostic on the Tools page, and an integration profile on the health endpoint (geo targeting, contract and revision, last sync times).
Fixes
- Card rendering no longer emits on-page notices or fatals on drifted data
- Upstream error messages are stripped, capped and escaped before reaching wp-admin
- Every contract failure now ends with an action: update the plugin, or report it
- Geo-targeting settings copy clarifies that it governs only the shortcode and block
Verification
840 tests, 2888 assertions. Verified end to end against a live staging tenant: 220 toplists across 44 pages and 542 brands across 22 pages, a forced 409 rejection with all local rows preserved, automatic recovery, rate-limit backoff, and the geo render gate unchanged across all three geo types.
See UPGRADING.md for the tenant-facing guide, including a table of every failure mode and what happens to your data.