robotstxt-manager/changelog.txt
2026-09-23 06:09:05 +00:00

547 lines
28 KiB
Text
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

== Changelog ==
= 1.8.1 =
_Release date: 2026-09-22_
**Fixed**
* Pending updates now appear on sites where the WordPress.org update check never completes (api.wordpress.org unreachable, blocked, or firewalled hosts — common on a lot of servers). `Robotstxt_Manager_Updater` moved from the write-side hook (`pre_set_site_transient_update_plugins`, which only fires when a full `wp_update_plugins()` cycle finishes) to the read-side filter (`site_transient_update_plugins`, which fires every time anything reads the update data: Plugins screen, Updates page, WP-CLI, auto-updates). Local versions are resolved from `get_plugins()` instead of the transient's `checked` list, which never exists in those environments. Diagnosed and verified end-to-end against the live store: update detection, listing (`wp plugin list`), dry-run, and the actual update run all work now even with api.wordpress.org blocked.
* Stale Manager-owned update entries no longer mask newer versions: WordPress persists the filtered read during a (failed) update check, so a previously injected entry can sit in the stored transient forever. The updater now recognizes its own entries (package URL host matches the store) and replaces or removes them with fresh catalog data — killing stale-version masking, phantom "update available" badges after updating, and expired download tokens in one guard. Entries from other update servers (bundled SDKs pointing elsewhere, WordPress.org) are never touched.
* Premium download-token exchanges are now cached (5-minute site transient, failures negatively cached for 1 minute): without the cache, every read of the update data while a premium update is pending triggered a blocking HTTP call to the store — several per admin page load.
* Opt-in data deletion on uninstall now also removes the cached subscriptions site transient (`robotstxt_manager_subscriptions`) — previously only the catalog transient was deleted, so subscription data could outlive the plugin when "Delete all plugin data" was enabled.
* A hardcoded "Subscribed" fallback label in the multi-license catalog pill (unknown subscription status with more than one license) is now translatable like every other pill label.
**Changed**
* PHPStan raised from level 9 to `max` (level 10); the two `mixed`-strictness findings it surfaced were fixed with real narrowing (`AUTH_KEY`/`AUTH_SALT` string checks in the encryption key derivation, the `plugins_api` slug check).
* `Robotstxt_Manager_Core_Client::get_subscriptions()` normalizes rows restored from the transient cache the same way `get_catalog()` does (string keys enforced) — resolves the single level-9 error surfaced by PHPStan 2.2.14; no behavior change.
* Development tooling updated via `composer update`: phpstan 2.2.8 → 2.2.14, phpstan-wordpress 2.0.3 → 2.0.4, wordpress-stubs 6.9.4 → 7.1.0, wp-hooks/wordpress-core 1.12.0 → 1.13.0, nikic/php-parser 5.8.0 → 5.9.0.
* Tests: dropped `ReflectionMethod::setAccessible()` calls (no-op since PHP 8.1, deprecated on PHP 8.5) so the suite runs notice-free on the maximum supported PHP version. Regression tests cover the "WordPress.org check never completed" transient shape, the `false` transient, stale own-entry replacement/removal, and download-token caching.
**Compatibility**
* WordPress: 4.4 - 7.1 (scan-verified 2026-09-22: wp-compat ladder clean from 4.4; verified across 4.4-7.1 with WordPress stubs 7.1.0 — no API newer than 4.4 in use, so 7.1 GA remains covered; 7.2 is not GA)
* PHP: 8.0 - 8.5 (scan-verified 2026-09-22: PHPCompatibility ladder 5.6-8.5 + manual audit — `str_contains()`, `str_starts_with()`, and `mixed` type hints keep the real floor at 8.0)
**Tests**
* PHPCS (WordPress-Core, WordPress-Docs, WordPress-Extra): pass
* PHPStan max (level 10) + wp-compat: pass
* PHPUnit: 152 tests, 401 assertions
* composer audit: no known CVEs
= 1.8.0 =
_Release date: 2026-08-24_
**Added**
* Multi-domain license awareness (with Core 1.14.0): `Robotstxt_Manager_Core_Client::get_subscriptions()` groups the flat `/me/subscriptions` rows per plugin slug — any-active status wins, the first bound domain is shown, and a `license_count` is kept. Catalog subscription pills render the count when a plugin has more than one license (e.g. "Subscribed @ example.com ×2").
**Tests**
* PHPCS (WordPress-Core, WordPress-Docs, WordPress-Extra): pass
* PHPStan level 9 + wp-compat: pass
* PHPUnit: 149 tests, 391 assertions
= 1.7.0 =
_Release date: 2026-08-18_
**Added**
* Per-domain license support (Core 1.11.0+): install/update downloads and the download-token exchange now send this site's normalized domain, so premium licenses bind to this site and the store rejects requests from other domains. Catalog subscription pills show the bound domain when the store reports one (e.g. "Subscribed @ example.com").
**Changed**
* Download failures now surface the store's own error message from the JSON body (e.g. the domain-mismatch explanation) instead of only "Download failed (HTTP 403)." — falls back to the bare code when no message is present.
**Tests**
* PHPCS (WordPress-Core, WordPress-Docs, WordPress-Extra): pass
* PHPStan level 9 + wp-compat: pass
* PHPUnit: 149 tests, 391 assertions
= 1.6.2 =
_Release date: 2026-08-18_
**Added**
* `ROBOTSTXT_MANAGER_NOTICED` presence constant (guarded with `defined()`, value `true`), defined when Manager loads: ecosystem plugins (Core, Mollie, …) detect an active Manager via `defined( 'ROBOTSTXT_MANAGER_NOTICED' )` instead of scanning the plugin list. The guard keeps a double-load or a conflicting definition from fatalling.
**Tests**
* PHPCS (WordPress-Core, WordPress-Docs, WordPress-Extra): pass
* PHPStan level 9 + wp-compat: pass
* PHPUnit: 147 tests, 386 assertions
= 1.6.1 =
_Release date: 2026-08-18_
**Fixed**
* "Cannot modify header information — headers already sent" warning after saving the settings: the form handler ran inside the page renderer (after output started). It now runs on `admin_init`, before any output, so the redirect succeeds.
= 1.6.0 =
_Release date: 2026-08-18_
**Added**
* WordPress Multisite compatibility: `Network: true` header forces network-wide activation. Menus appear in the network admin on Multisite, in the regular admin on single-site. All options and transients use network-level storage (`get_site_option` / `get_site_transient`). Settings form handles submission manually (the Settings API `options.php` does not handle network options).
**Fixed**
* API key not saving when the browser auto-fills the password field with the stored encrypted value — the sanitizer now detects already-encrypted input (`v2:` prefix) and returns it as-is instead of failing UUID validation.
* "Test connection" validated against the public catalog endpoint (`/plugins`), so invalid API keys reported "Connected". Now uses the authenticated `/me/subscriptions` endpoint; a 401 response produces "Invalid API key".
* "Test connection" read the key from saved options only — entering a new key and testing before saving always tested the old (or empty) key. The AJAX handler now accepts the key from the form field.
**Added**
* `settings_errors()` call on the settings page so validation messages (e.g. "invalid API key format") are actually visible to the user.
* Registration link (`https://www.robotstxt.software/wp-login.php?action=register`) in the API key field description.
**Tests**
* PHPCS (WordPress-Core, WordPress-Docs, WordPress-Extra): pass
* PHPStan level 9 + wp-compat: pass
* PHPUnit: 146 tests, 385 assertions
= 1.5.0 =
_Release date: 2026-08-17_
**Changed**
* Plugin identity now points at the ROBOTSTXT software site: Plugin URI and the new Update URI header are `https://www.robotstxt.software/plugins/robotstxt-manager/`, Author URI is `https://www.robotstxt.software/`, and the readme.txt full-changelog link points to the same page.
* Contributors list reordered: robotstxt first, then javiercasares.
* readme.txt changelog section trimmed to the 3 latest versions per the documentation template.
* Tooling aligned with the declared real floors: PHPCS `testVersion` and the preflight scan range now cover PHP 8.0-8.5 (previously 7.4-8.5 / 8.4-8.5).
**Compatibility**
* WordPress: 4.4 - 7.1 (scan-verified: wp-compat ladder clean from 4.4; 10 errors at 4.0)
* PHP: 8.0 - 8.5 (scan-verified: PHPCompatibility ladder + manual audit — `mixed` hints and `str_contains()` are the 8.0 floor)
**Tests**
* PHPCS (WordPress-Core, WordPress-Docs, WordPress-Extra): pass
* PHPStan level 9 + wp-compat: pass
= 1.4.0 =
_Release date: 2026-08-17_
**Changed**
* Premium package URLs use short-lived download tokens (Core 1.9.0+ `POST /me/download-token`): the updater exchanges the account key each update cycle and embeds the 15-minute token instead of the API key, so the long-lived key no longer sits in the `update_plugins` transient or server access logs. Falls back to the API-key flow automatically on older Core. (Closes the 0.5.1 audit finding.)
**Compatibility**
* WordPress: 4.4 - 7.1 (scan-verified: wp-compat clean from 4.4)
* PHP: 8.0 - 8.5 (scan-verified: PHPCompatibility + manual feature audit)
= 1.3.1 =
_Release date: 2026-08-17_
**Added**
* Localized descriptions: catalog rows and the "View details" modal show the description in the logged-in admin's language (`get_user_locale()`, falling back to the site locale), using the new `description_translations` map from Core 1.8.0+; missing translations fall back to the English default.
**Changed**
* Plugin version 1.3.0 → 1.3.1. No database schema changes (no custom tables).
**Compatibility**
* WordPress: 4.4 - 7.1 (scan-verified: wp-compat clean from 4.4)
* PHP: 8.0 - 8.5 (scan-verified: PHPCompatibility + manual feature audit)
= 1.3.0 =
_Release date: 2026-08-17_
**Highlights**
* The catalog now works **without an API key**: only the Store URL is required. The free catalog, prices, and product pages are public on Core, so a fresh install shows the store immediately. An info notice invites visitors to create their free account at the store to get a personal API key (linking subscriptions and unlocking premium plugins and updates).
**Changed**
* Action buttons follow subscription state: premium plugins without an active subscription show **Buy** only (Install/Update hidden — the download would be rejected); with an active or in-grace subscription (and for free plugins) they show **Install / Activate / Update** as usual. Previously premium rows always offered Install alongside Buy.
* The API-key setting description clarifies the key is optional for browsing, required for subscriptions/premium.
* Plugin version 1.2.1 → 1.3.0. No database schema changes (no custom tables).
**Compatibility**
* WordPress: 4.4 - 7.1 (scan-verified: wp-compat clean from 4.4)
* PHP: 8.0 - 8.5 (scan-verified: PHPCompatibility + manual feature audit)
= 1.2.1 =
_Release date: 2026-08-17_
**Added**
* Plugin icons in the catalog: each row shows the plugin's `icon_url` from the Core API at 64×64 px (1:1, lazy-loaded) in the Plugin column; plugins without an icon get a plain white 64×64 placeholder.
**Changed**
* Plugin version 1.2.0 → 1.2.1. No database schema changes (no custom tables).
**Compatibility**
* WordPress: 4.4 - 7.1 (scan-verified: wp-compat clean from 4.4)
* PHP: 8.0 - 8.5 (scan-verified: PHPCompatibility + manual feature audit)
= 1.2.0 =
_Release date: 2026-08-15_
**Highlights**
* Subscription status, visible at a glance (Phase 4). The catalog now pulls the account's subscriptions from Core (`GET /me/subscriptions`, cached for one hour; refreshed on catalog refresh and key change) and shows a pill next to each premium plugin's price: Subscribed (with "N days left" when expiring within 14 days), Payment failed, Cancelled, or Expired. Admin notices at the top of the page warn about failed payments, subscriptions expiring within 14 days, and expired subscriptions whose plugin is still active on this site.
**Changed**
* Plugin version 1.1.0 → 1.2.0. No database schema changes (no custom tables).
**Compatibility**
* WordPress: 4.4 - 7.1 (scan-verified: wp-compat clean from 4.4)
* PHP: 8.0 - 8.5 (scan-verified: PHPCompatibility + manual feature audit)
= 1.1.0 =
_Release date: 2026-08-15_
**Added**
* Cascade dependency install: when installing a plugin whose catalog entry declares `requires_plugins` (Core 1.7.0+), Manager first installs every missing dependency — ecosystem plugins through the store flow, WordPress.org plugins (e.g. Action Scheduler) via `plugins_api` and their repository ZIPs — dependencies of dependencies first, cycle-safe. The success notice lists what was installed; failures name the dependency and abort before the main install. Catalog rows show a "Requires: …" hint in the detail line.
**Changed**
* Plugin version 1.0.0 → 1.1.0. No database schema changes (no custom tables).
**Compatibility**
* WordPress: 4.4 - 7.1 (scan-verified: wp-compat clean from 4.4)
* PHP: 8.0 - 8.5 (scan-verified: PHPCompatibility + manual feature audit)
= 1.0.0 =
_Release date: 2026-08-15_
**Highlights**
* Catalog page redesign. The table columns are now Plugin · Version · Requires WP · Requires PHP · Price · Action · Status: the Type column is merged into Price ("Free" for cost-0 plugins, the annual price for premium), the version moved into its own column, the action buttons sit before the status, and "Local state" is renamed "Status". Each plugin's detail row — website link first, then its description — is now **always open** (the click-to-expand toggle is gone). An intro paragraph after the page title explains the store, and Support + Payments sections below the table describe how to get help and how the annual-subscription model works (automatic renewal, cancellation from the account page, Mollie processing).
**Changed**
* Plugin version 0.6.0 → 1.0.0 — first stable release. No database schema changes (no custom tables).
**Compatibility**
* WordPress: 4.4 - 7.1 (scan-verified: wp-compat clean from 4.4)
* PHP: 8.0 - 8.5 (scan-verified: PHPCompatibility + manual feature audit)
= 0.6.0 =
_Release date: 2026-08-15_
**Added**
* Spanish (es_ES) and Catalan (ca) translations — all 73 admin strings, regenerated POT included.
* "Refresh catalog" is rate-limited to 6 refreshes per minute per user (transient bucket), redirecting back with an error notice when exceeded; the limit rejects before clearing any cache (Phase 6 hardening).
**Changed**
* Plugin version 0.5.3 → 0.6.0. No database schema changes (no custom tables).
**Compatibility**
* WordPress: 4.4 - 7.1 (scan-verified: wp-compat clean from 4.4)
* PHP: 8.0 - 8.5 (scan-verified: PHPCompatibility + manual feature audit)
= 0.5.3 =
_Release date: 2026-08-15_
**Added**
* Authenticated encryption for the stored API key (same encrypt-then-MAC scheme as Core 1.6.0): tampered payloads fail closed, and encryption refuses to run without real WordPress salts. Legacy-stored keys keep decrypting and re-encrypt on the next save.
**Changed**
* Plugin version 0.5.2 → 0.5.3. No database schema changes (no custom tables).
**Compatibility**
* WordPress: 4.4 - 7.1 (scan-verified: wp-compat clean from 4.4)
* PHP: 8.0 - 8.5 (scan-verified: PHPCompatibility + manual feature audit)
= 0.5.2 =
_Release date: 2026-08-15_
**Fixed**
* Manager-panel Install/Update failed while the native WordPress updater worked. Two defects in the panel's upgrader path (`Robotstxt_Manager_Installer`):
1. `ensure_plugin_functions()` required only `class-wp-upgrader.php`, which since the WordPress 5.3 class split no longer defines `Plugin_Upgrader` (it lives in `class-plugin-upgrader.php`). In a stock `admin-post.php` context nothing else loads it → fatal error. Both files are now required.
2. The overwrite flag was passed to `Plugin_Upgrader::install()` as `'overwrite'`, but current WordPress reads `'overwrite_package'` — the unknown key was silently discarded, `clear_destination` stayed false, and the install failed against the existing folder. Both keys are now passed (the unused one is ignored by `wp_parse_args()`), keeping compatibility across WP versions.
* Verified end-to-end on the live store: panel-path download → overwrite-install succeeds, plugin remains active.
**Changed**
* Plugin version 0.5.1 → 0.5.2. No database schema changes (no custom tables).
**Compatibility**
* WordPress: 4.4 - 7.1 (scan-verified: wp-compat clean from 4.4)
* PHP: 8.0 - 8.5 (scan-verified: PHPCompatibility + manual feature audit)
= 0.5.1 =
_Release date: 2026-08-15_
**Highlights**
* Stabilization release after a full code + security audit (fresh-context review per AGENTS-deploy.md) and full-range compatibility scans (PHPCompatibility 5.68.5, wp-compat laddering). Declared requirements now match the real floors: **WordPress 4.4+** (was 4.7) and **PHP 8.0+** (was 7.4 — the updater's `mixed` type hints and `str_contains()` require 8.0; previously under-declared, which would have been a fatal on 7.4).
**Fixed**
* `test_connection()` reported "Connected." on non-200 responses and `get_catalog()` cached auth failures as an empty catalog for the full TTL. Both now check the HTTP status; non-200 responses return an error/empty and are never cached.
* `site_domain()` used `ltrim( $host, 'www.' )`, which strips a character set and mangles hosts starting with `w` (e.g. `webdev.example.com` → `ebdev…`, breaking premium package URLs). Now strips only the literal `www.` prefix.
* Native-update integration no longer injects premium `response` entries when no decryptable API key exists (the native updater would download into a 403).
* Opt-in uninstall: also deletes the `update_plugins` site transient (premium entries embed the API key in the package URL — the plaintext copy must not outlive the plugin) and stops deleting a phantom `robotstxt_manager_db_version` option nothing ever wrote.
**Changed**
* API-key setting validates the format (UUID-like, 8127 chars) before storing, rejecting mangled input at save time instead of failing later at connection time.
* Plugin version 0.5.0 → 0.5.1. No database schema changes (no custom tables).
**Compatibility**
* WordPress: 4.4 - 7.1 (scan-verified: wp-compat clean from 4.4)
* PHP: 8.0 - 8.5 (scan-verified: PHPCompatibility + manual feature audit)
= 0.5.0 =
_Release date: 2026-08-14_
**Highlights**
* Native WordPress update integration: installed catalog plugins feed into WordPress's own update system. The standard "Update available" badge appears on the Plugins screen, and clicking "Update now" runs the normal WordPress updater with the ROBOTSTXT store as the download source — no custom UI.
**Added**
* `Robotstxt_Manager_Updater` — injects catalog update data into the `update_plugins` transient (`pre_set_site_transient_update_plugins`): a `response` entry for outdated catalog plugins (package = the store's download proxy; premium entries append the account API key as `api_key`, which Core 1.5.0+ accepts as a query parameter because the native upgrader cannot send headers) and a `no_update` entry for up-to-date ones. Entries injected by a plugin's own bundled SDK are never overwritten.
* `plugins_api` filter — the "View details" modal for catalog slugs is served from catalog data (name, version, requires, tested, homepage, description section, icon, banner).
* "Refresh catalog" now also deletes the `update_plugins` transient so the update badges re-evaluate immediately.
**Changed**
* Plugin version 0.4.0 → 0.5.0. No database schema changes (no custom tables).
**Compatibility**
* WordPress: 7.0 - 7.1 (declared floor of the ecosystem; scan-confirmed lower)
* PHP: 8.4 - 8.5 (declared floor of the ecosystem; scan-confirmed lower)
= 0.4.0 =
_Release date: 2026-08-14_
**Highlights**
* Click-to-expand rows in the catalog table: clicking the ▸ next to a plugin's name reveals a detail row with the plugin's description and a link to its website on the store, so users can understand what each plugin does before installing. Pure CSS toggle (`:has()` + hidden checkbox) — no JavaScript, consistent with the classic-flow philosophy.
**Added**
* Detail row under each catalog entry, shown when the entry has a description or a website URL (Core 1.4.2+ exposes `description` in the catalog list; entries without either keep a plain, non-expandable row).
* "Visit website" link using `page_url` (falling back to `homepage`), opened in a new tab with `rel="noopener noreferrer"`.
**Changed**
* Plugin version 0.3.1 → 0.4.0. No database schema changes (no custom tables).
**Compatibility**
* WordPress: 7.0 - 7.1 (declared floor of the ecosystem; scan-confirmed lower)
* PHP: 8.4 - 8.5 (declared floor of the ecosystem; scan-confirmed lower)
= 0.3.1 =
_Release date: 2026-08-14_
**Changed**
* Default store URL is now `https://www.robotstxt.software` (was `https://plugins.robotstxt.es`). Applies to new activations and fresh installs; existing saved URLs are preserved. The catalog API at the new domain is verified live.
* Plugin version 0.3.0 → 0.3.1.
**Compatibility**
* WordPress: 7.0 - 7.1 (declared floor of the ecosystem; scan-confirmed lower)
* PHP: 8.4 - 8.5 (declared floor of the ecosystem; scan-confirmed lower)
= 0.3.0 =
_Release date: 2026-08-13_
**Highlights**
* Classic install/activate/update flow: the catalog actions now run on a full page load (`admin-post.php` with per-action-and-slug nonces) and report the outcome through standard admin notices (green success / red error) at the top of the page, replacing the previous AJAX flow.
**Added**
* Activate action — installed-but-inactive plugins show an "Activate" button that calls `activate_plugins()`.
* Update action — plugins with a newer catalog version show an "Update" button that re-downloads the ZIP and runs the `Plugin_Upgrader` with `overwrite` enabled.
* Install/Activate/Update buttons are nonce links (`robotstxt_manager_{action}_{slug}` nonce actions) to `admin-post.php`, all `manage_options`-gated.
* Redirect-based notices: handlers redirect back to the catalog page carrying `robotstxt_manager_result` (success/error) and `robotstxt_manager_message`, rendered as dismissible standard notices.
**Changed**
* `Robotstxt_Manager_Installer` rewritten from an AJAX handler to three admin-post handlers (`robotstxt_manager_install`, `robotstxt_manager_activate`, `robotstxt_manager_update`); download logic (Bearer account-key auth, ZIP validation, temp-file cleanup) unchanged.
* Removed the AJAX endpoint, `manager-install.js`, and its script localization.
* Plugin version 0.2.0 → 0.3.0.
**Compatibility**
* WordPress: 7.0 - 7.1 (declared floor of the ecosystem; scan-confirmed lower)
* PHP: 8.4 - 8.5 (declared floor of the ecosystem; scan-confirmed lower)
= 0.2.0 =
_Release date: 2026-08-12_
**Highlights**
* Phase 2: the Install action. Free and premium plugins can be installed directly from the ROBOTSTXT catalog into the local site, authenticated with the account-level API key (requires Plugins Core 1.4.0+).
**Added**
* `Robotstxt_Manager_Installer` — AJAX handler (`wp_ajax_robotstxt_manager_install_plugin`, nonce-protected, `manage_options`-gated) that resolves the plugin in the remote catalog, downloads the ZIP (free plugins with a published public `download_url` directly; everything else via Core's authenticated `/download` endpoint with an `Authorization: Bearer <account API key>` header), validates the archive (ZIP magic bytes), installs it via `Plugin_Upgrader`, and cleans up the temp file on every path.
* `public/js/manager-install.js` — wires the Install buttons to the AJAX action with in-flight state and result rendering.
* Catalog view: enabled Install buttons for compatible plugins (free and premium).
**Changed**
* Plugin version 0.1.3 → 0.2.0. Description updated to reflect Phase 2.
**Compatibility**
* WordPress: 7.0 - 7.1 (declared floor of the ecosystem; scan-confirmed lower)
* PHP: 8.4 - 8.5 (declared floor of the ecosystem; scan-confirmed lower)
= 0.1.3 =
_Release date: 2026-08-12_
**Changed**
* Version bumped 0.1.2 → 0.1.3.
* `composer.json` PHP requirement updated from `>=8.4` to `>=7.4` to match the real PHPCompatibility scan floor and the plugin header.
* `composer.lock` regenerated.
* `.pot` file regenerated for v0.1.3.
**Compatibility**
* WordPress: 4.7 - 7.1
* PHP: 7.4 - 8.5
**Tests**
* PHP Coding Standards: PHP_CodeSniffer 3.13.6
* WordPress Coding Standards: WPCS 3.4.1
* PHPStan: level 9, 0 errors
* PHPCompatibility: 7.48.5 clean
* wp-compat: 0 errors against WP 4.7
= 0.1.2 =
_Release date: 2026-08-12_
**Highlights**
* Documentation refresh: readme.txt and changelog.txt aligned to the standard templates.
**Changed**
* Version bumped 0.1.1 → 0.1.2.
* readme.txt: added Extra Configurations section, Automatic download subsection in Installation, Previous versions link in Changelog.
* changelog.txt: 0.1.1 entry reformatted to follow the standard template (Highlights, Changed, Compatibility, Tests).
* PHPCompatibility scan (5.68.5): real PHP floor is **7.2**. Project declares **8.4** (ecosystem exception).
* wp-compat scan against WordPress 4.7: **zero WPCompat errors**. Real WP floor is **≤ 4.7**. Project declares **7.0** (ecosystem support window).
**Compatibility**
* WordPress: 4.7 - 7.1
* PHP: 7.4 - 8.5
**Tests**
* PHP Coding Standards: PHP_CodeSniffer 3.13.6
* WordPress Coding Standards: WPCS 3.4.1
* PHPStan: level 9, 0 errors
* PHPCompatibility: 8.48.5 clean (real floor 7.2)
* wp-compat: 0 errors against WP 4.7
= 0.1.1 =
_Release date: 2026-08-12_
**Highlights**
* Compatibility scans completed and documented. No code changes.
**Changed**
* Version bumped 0.1.0 → 0.1.1.
* PHPCompatibility scan (5.68.5) completed: real PHP floor is **7.2** (highest-required feature is `object` return type, introduced in PHP 7.2). The project deliberately declares **8.4** to match the ROBOTSTXT ecosystem — this is a documented exception to `AGENTS-deploy.md` §"PHP compatibility check".
* wp-compat scan against WordPress 4.7: **zero WPCompat errors**. The Manager's code uses only WordPress APIs available since 4.7 or earlier. The real WordPress floor is **≤ 4.7**; the project declares `Requires at least: 7.0` to match the ecosystem support window.
* `.pot` file regenerated for v0.1.1.
**Compatibility**
* WordPress: 4.7 - 7.1
* PHP: 7.4 - 8.5
**Tests**
* PHP Coding Standards: PHP_CodeSniffer 3.13.6
* WordPress Coding Standards: WPCS 3.4.1
* PHPStan: level 9, 0 errors
* PHPCompatibility: 8.48.5 clean (real floor 7.2)
* wp-compat: 0 errors against WP 4.7
= 0.1.0 =
_Release date: 2026-08-12_
**Highlights**
* Phase 1 scaffold of Manager (by ROBOTSTXT) — client-side dashboard for the ROBOTSTXT plugin ecosystem. Read-only catalog view with local install-state resolution.
**Added**
* Plugin header, autoloader (Composer with manual fallback), lifecycle hooks, constants (`ROBOTSTXT_MANAGER_VERSION`, `ROBOTSTXT_MANAGER_DIR`, `ROBOTSTXT_MANAGER_URL`, `ROBOTSTXT_MANAGER_BASENAME`).
* `Robotstxt_Manager_Loader` action/filter queue (identical pattern to Core/Mollie/Sync).
* `Robotstxt_Manager_Encryption` — AES-256-CBC encrypt/decrypt for the API key, using WordPress `AUTH_KEY` + `AUTH_SALT` for key derivation. Same pattern as Core's Forgejo token encryption.
* `Robotstxt_Manager_Core_Client` — HTTP client for the remote Plugins Core REST API. Authenticated via `Authorization: Bearer <key>` header. Implements `test_connection()`, `get_catalog()` (cached in transient), `clear_catalog_cache()`.
* `Robotstxt_Manager_Settings` — Settings API page with Store URL, API Key (masked, encrypted, last-4 hint, delete-on-empty preservation), Catalog Cache TTL, Data-on-Uninstall opt-in. AJAX "Test connection" button with nonce + capability check.
* `Robotstxt_Manager_Admin` — Top-level admin menu (label "ROBOTSTXT") with catalog table view. Resolves local install/active/update state for every catalog entry using `get_plugins()` + `is_plugin_active()`. "Refresh catalog" admin-post action.
* `uninstall.php` with opt-in data deletion (default: preserve all options and transients).
* Per-project tooling: Composer, PHPCS + WPCS + PHPCompatibility, PHPStan level 9 + wp-compat, PHPUnit. `bin/preflight.sh` and `bin/deploy.sh`.
**Compatibility**
* WordPress: 4.7 - 7.1
* PHP: 7.4 - 8.5
**Tests**
* Phase 1 scaffold — plugin-header tests included. Additional tests for Core client, settings, and admin rendering in follow-up phases.