=== SearchChamp SEO ===
Contributors: searchchamp
Tags: seo, ai visibility, content optimization, schema, indexnow
Requires at least: 6.0
Tested up to: 7.1
Requires PHP: 8.0
Stable tag: 0.12.0
License: GPLv2 or later
License URI: https://www.gnu.org/licenses/gpl-2.0.html

Connect WordPress to SearchChamp for AI-powered SEO analysis, one-click audit fixes, and AI-search visibility.

== Description ==

SearchChamp SEO is the WordPress companion for the [SearchChamp](https://searchchamp.com) platform.
It is a **connector, not a standalone SEO plugin**: the analysis runs on the SearchChamp service,
and this plugin is what lets that service read your content and write approved changes back.

You need a SearchChamp account and a Site Key. Without one, the plugin installs and activates
cleanly but has nothing to show you.

== Screenshots ==

1. The block-editor sidebar: content score, live search-result preview, and the focus keyword, without leaving the post.
2. The classic editor meta box: focus keyword, meta title and description with character counts, and a live desktop/mobile search-result preview.
3. Bulk Meta Editor: edit the focus keyword, title and description for every post from one screen, with per-row and bulk AI generation.
4. A SearchChamp SEO column on the posts and pages list, showing at a glance which posts still need meta.
5. AI Search Readiness: a score for how legible this site is to AI search, with Markdown twins, llms.txt, an AI-robots policy and AI-crawler tracking in one place.
6. Fix History: every change SearchChamp applied, with status, filtering, CSV export and one-click undo.
7. Connection settings: paste a Site Key, test the connection, and choose which optional features are on.

= What it actually does =

* **On-page SEO meta box** — set the focus keyword, meta title, and meta description from the post
  editor, with a live SERP snippet preview and character counts. Works in both the classic editor
  and a Gutenberg sidebar.
* **Writes to your existing SEO plugin.** If Yoast SEO, Rank Math, All in One SEO, or SEOPress is
  active, SearchChamp writes to *its* meta keys instead of emitting a competing title and canonical
  tag. Only when no SEO plugin is active does SearchChamp output its own.
* **Bulk meta editor** — review and fill missing titles and descriptions across your posts and
  pages from one screen, 30 rows at a time, with an optional AI generation step. "Generate All"
  runs every row on the page that is not already complete; there is no separate per-run cap on
  this screen.
* **Bulk generation from the Posts list** — the same AI step offered as a bulk action on the
  standard Posts and Pages screens. This one *is* capped, at 10 posts per run: anything beyond the
  cap is left unprocessed and reported in a notice, so a "Select All" cannot fan out into hundreds
  of blocking AI calls in one request.
* **One-click audit fixes** — SearchChamp's site audit sends fixes to your site over a signed
  channel; each applied fix is recorded in a Fix History table with the previous value, so it can
  be undone. Fix History exports to CSV.
* **AI-search visibility** — a dashboard widget and a dedicated page showing whether the five
  tracked AI engines (ChatGPT, Perplexity, Claude, Gemini, Grok) mention or cite your site, plus a
  log of AI crawler hits on your pages.
* **AI Search Readiness tools** — an llms.txt generator, Markdown "twin" copies of your pages for
  AI crawlers, a robots.txt AI-access policy, and a self-test that checks each one is actually
  reachable. Each is off by default and is enabled individually. One exception worth knowing:
  once your site is connected, SearchChamp can publish an llms.txt file to it over the signed
  channel without a separate toggle. That write is authenticated and rate-limited, and you can
  replace or remove the file from this screen at any time.
* **IndexNow submission** — optionally ping IndexNow when you publish, so Bing (and the surfaces
  that read from it, notably ChatGPT Search and Copilot) learn about the URL quickly. Off by
  default. This is a Bing-family fast path only: it does **not** reach Google, Gemini, Perplexity,
  or Claude.
* **Content publishing** — publish drafts written in SearchChamp straight into WordPress, with
  images imported into your media library and converted to native block markup.
* **Schema / sameAs** — contributes entity `sameAs` links to your existing structured data by
  reference rather than emitting a second, competing Organization node.

= What it does not do =

Kept explicit so the feature list above stays honest:

* It does **not** track keyword rankings inside WordPress.
* It does **not** work without a SearchChamp account.
* It does **not** rewrite or generate page content on its own — every change is one you or the
  platform explicitly approves.
* IndexNow is not a Google submission API. There isn't one.

= Requirements =

* WordPress 6.0 or higher
* PHP 8.0 or higher
* A [SearchChamp account](https://searchchamp.com) with a Site Key

= Getting Started =

1. Install and activate the plugin.
2. Go to **SearchChamp SEO** in the WordPress admin sidebar.
3. Paste your **Site Key** (in SearchChamp: **Integrations → WordPress → Generate**).
4. Click **Test Connection**, then **Save Settings** — the connection-health card should read
   "Connected".
5. Open any post or page — the SearchChamp SEO panel is below the content editor, and in the
   block editor's sidebar.

= Privacy and data =

On a default install this plugin makes no outbound requests at all: nothing leaves your site until
you paste a Site Key and connect.

There is one exception, and it is one you switch on yourself. **IndexNow** (service 2 below)
submits published URLs to Microsoft/Bing and works independently of SearchChamp, so turning it on
without connecting still sends data off your site. Nothing else does.

See the **External services** section below for exactly what is sent, to whom, and when.

== External services ==

This plugin relies on external services. Each is listed with what it does, what data is sent, when
it is sent, and the provider's terms.

= 1. The SearchChamp platform (required) =

The service this plugin is a connector for. Operated by Atlio Information Technology.

Endpoints: `https://api.searchchamp.com` and `https://features-api.searchchamp.com`. An
administrator may point the plugin at the development gateway
(`https://api-dev.searchchamp.dev`, `https://features-api-dev.searchchamp.dev`) under Advanced
settings.

**What is sent, and when:**

* *When you connect, or rotate the key* — your site URL, the Site Key you pasted, and the plugin
  version.
* *When you open the settings, dashboard widget, or AI Visibility page* — your site URL and your
  API key, to fetch connection status, your project list, audit health, and AI-visibility results.
* *When you request an SEO score or AI-generated meta* (from the editor, the bulk meta editor, or
  the posts list) — the post title, the focus keyword, and either the post's HTML body or up to
  600 characters of its text, plus the project id. This is the content being analysed; it is sent
  only when you click the control that asks for the analysis.
* *When you view page issues or rescan a page* — the page's URL.
* *Once a day, if connected* — a heartbeat containing only the connection id and the plugin
  version, so the dashboard can show whether the site is still reachable.
* *When you undo a published post* — the publish-history id.
* *When SearchChamp publishes a draft into your site* — your site downloads the images referenced
  by that draft. They are fetched from `https://features-api.searchchamp.com` (or
  `https://features-api-dev.searchchamp.dev`), and from SearchChamp's article-asset storage on
  Amazon Web Services S3 in the eu-west-1 region — `s3.eu-west-1.amazonaws.com` — where the
  bucket hostnames take the form `atlio-cw-assets-<environment>.s3.eu-west-1.amazonaws.com`.
  That storage is operated by Atlio Information Technology for SearchChamp. Only those hosts are
  accepted: any other address, including a redirect to one, is refused and the image is not
  imported. The request carries no data about your site, and nothing is uploaded.
* *When you delete the plugin* — a request to revoke this site's connection.
* *When WordPress checks for plugin updates* — a request to `https://artifacts.searchchamp.dev/wordpress-plugin/searchchamp-seo` (the update channel, operated by Atlio Information Technology on AWS S3 in the eu-west-1 region) for the current version and the download URL of a newer release; when an update is available and you choose to install it, the release zip is downloaded from `https://artifacts.searchchamp.dev`. The request carries no data about your site.

**What is never sent:** post content is transmitted only for the specific post you asked to be
analysed or published. There is no background crawl of your site, no visitor analytics, no user
account data, and no telemetry about your WordPress install.

Terms of service: https://searchchamp.com/legal/terms — Privacy policy:
https://searchchamp.com/legal/privacy

= 2. IndexNow (optional, off by default) =

IndexNow is an open protocol operated by Microsoft/Bing that lets a site announce new or updated
URLs. It is enabled only if you switch on **IndexNow** in the plugin's AI Search Readiness
settings.

Endpoint: `https://api.indexnow.org/indexnow`

**What is sent, and when:** when a post is published or updated, the plugin sends your site's
hostname, the URL that changed, a randomly generated verification key, and the public location of
that key file. No post content, no account data, and no SearchChamp credentials are sent to
IndexNow. This works independently of SearchChamp — it functions even when the plugin is not
connected.

The verification key is also served publicly from your site at `https://<your-site>/<key>.txt`, as
the IndexNow protocol requires.

IndexNow documentation and terms: https://www.indexnow.org/ and https://www.bing.com/indexnow —
Microsoft privacy statement: https://privacy.microsoft.com/privacystatement

= 3. AI-crawler visibility beacon (optional, off by default, requires a connection) =

Records when a known AI crawler fetches one of your pages, which is what powers the AI-crawler
counts on the AI Visibility screen. Enabled only if you switch on **AI crawler tracking**, and
only functional after the site is connected — the destination and its access token are issued by
SearchChamp during connection.

Endpoint: an ingest address issued to your site by SearchChamp when you connect. It is returned by
`https://features-api.searchchamp.com` and stored on your site; it always points at infrastructure
operated by Atlio Information Technology. The plugin sends the beacon only to the address
SearchChamp issued — the destination is not user-configurable, and the beacon is not sent at all
until one has been issued.

**What is sent, and when:** on a front-end page request **whose user agent matches a known AI
crawler** (GPTBot, ChatGPT-User, PerplexityBot, ClaudeBot, Google-Extended, Bytespider, cohere-ai,
and similar), the plugin sends the requested URL, the crawler's user-agent string, and **the IP
address the request came from**, together with the site's ingest token.

A request from an ordinary human visitor never matches the crawler list and never triggers this
call. No page content is sent.

Terms of service: https://searchchamp.com/legal/terms — Privacy policy:
https://searchchamp.com/legal/privacy

= 4. Plugin update check — self-hosted builds only =

The copy of this plugin distributed from wordpress.org is updated by wordpress.org and **makes no
update requests of its own**. The self-hosted build downloaded directly from searchchamp.com
checks `https://features-api.searchchamp.com/v1/wp/plugin/update-manifest` at most once every six hours
to learn whether a newer version exists. That request is anonymous: it carries no site URL, no
account identifier, and no API key — only a `User-Agent` naming the plugin version.

= 5. Your own site (loopback self-test) =

Not a third party, but listed for completeness because it does make HTTP requests. The **AI Search
Readiness** screen has a self-test that checks your llms.txt, Markdown copies and robots.txt policy
are genuinely reachable. It does this by requesting **your own site's URLs**, built from your
WordPress home URL, using an AI-crawler user-agent so the result reflects what a crawler would see.

**What is sent, and when:** only when you click **Run self-test**. The requests go to your own
domain and nowhere else, and carry no credentials and no post content.

== Third-party libraries ==

This plugin bundles [league/html-to-markdown](https://github.com/thephpleague/html-to-markdown)
(MIT licence), used to generate the Markdown "twin" copies of pages for AI crawlers.

== Installation ==

1. Install through **Plugins → Add New**, or upload the plugin folder to `/wp-content/plugins/`.
2. Activate the plugin through the **Plugins** screen.
3. Go to **SearchChamp SEO** in the admin sidebar and paste your Site Key.

== Frequently Asked Questions ==

= Do I need a paid account? =

You need a SearchChamp account. The plugin is a connector — on its own it has no data to display.

= Where do I find my Site Key? =

Log in to [searchchamp.com](https://searchchamp.com), go to **Integrations → WordPress**, and click
**Generate** to create a Site Key for this site.

= I already use Yoast / Rank Math / AIOSEO / SEOPress. Will this conflict? =

No. SearchChamp detects an active SEO plugin and writes to that plugin's own fields rather than
emitting a second title, canonical, or Organization node. Your existing plugin stays in charge of
the output; SearchChamp edits what it publishes.

= Does this plugin send my content anywhere? =

Only the specific post you ask it to analyse or publish, and only at the moment you ask. See
**External services** above for the full list.

= Does it track keyword rankings? =

Not inside WordPress. Rank tracking lives in the SearchChamp dashboard.

= Can I undo a fix that was applied to my site? =

Yes. Every applied fix is recorded in **Fix History** along with the value it replaced, and can be
reverted from that screen.

= Does this plugin support custom post types? =

Yes — the SEO meta box can be extended to any post type with the
`atlio_seo_meta_box_post_types` filter.

= I used to have this plugin as "Atlio SEO". What changed? =

The name and the text domain, nothing else. Your settings, connection, SEO meta, and fix history
are stored under the same keys and carry over untouched. If you have a translation file named
`atlio-seo-<locale>.mo`, it is still picked up.

= Who operates the service this plugin connects to? =

Atlio Information Technology, which operates SearchChamp. The plugin contacts
`api.searchchamp.com` and `features-api.searchchamp.com`; both are disclosed in full under
**External services** above. Some internal identifiers inside the plugin (option names, database
keys, the `atlio/v1` REST namespace) still carry the original company name — they are deliberately
left unchanged so that upgrading never loses your saved settings or history.

== Changelog ==

= 0.12.0 =
* **Housekeeping release for the WordPress.org listing. Nothing you use changes.** Every feature, setting, screen and stored value behaves exactly as it did in 0.11.0.
* **The plugin's admin messages now stay where they belong.** The "not connected" prompt and the "make this site AI-search ready" tip used to appear on every screen in your dashboard. The connection prompt now appears only on the Dashboard and the Plugins screen; the AI-search tip appears only on SearchChamp SEO's own pages. Both are still dismissible and still stay dismissed. The message shown when a bulk SEO-meta run finishes now appears only on the posts or pages list you ran it from — it could previously be made to appear on any admin screen by hand-editing the web address.
* **The message shown when two copies of the plugin are installed can now be dismissed**, and appears only on the Dashboard and the Plugins screen — the place where you fix it.
* Changed (developers): the five remaining inline `<script>` and `<style>` blocks on the classic SEO meta box, the Page Issues panel and the Bulk SEO Meta screen are now enqueued as real files under `assets/`, with their nonces and copy passed via `wp_localize_script()`. No behaviour changes; the screens work exactly as before.
* Changed (developers): the post HTML sent for content scoring is now filtered with `wp_kses_post()`, and the rate-limit address used by the Markdown-twin route is sanitised. For any user without `unfiltered_html` WordPress applies the identical filter on save, so the score is computed over the same HTML that would be stored.
* Changed (developers): the safeguard that checks no SearchChamp asset address is left behind in a published article now derives that address from the same setting that authorises image downloads, instead of a second hardcoded copy. On a site that had repointed the asset host, the safeguard was looking for the wrong one.
* Changed (developers): the download now includes `composer.json` and `composer.lock` alongside the bundled `vendor/` directory, so the third-party code shipped with the plugin and its exact versions are visible without unpacking anything. The WordPress.org listing banners, icons and screenshots no longer ride along inside the download — they belong to the listing, not the plugin.

= 0.11.0 =
* **New: SearchChamp can now update an article it has already published, instead of publishing a second copy of it.** Until now this plugin could only ever create a post. When SearchChamp was asked to refresh an article on your site there was no way to tell the plugin *which* post to edit, so the request either produced a duplicate post or failed outright. The plugin now accepts the target post and edits that post, and only that post. WordPress records its own revision of the article as it was beforehand, so the previous version is in the editor's revision history if you want it back.
* **Your web address never changes.** Re-publishing an article updates its content, not its permalink, so existing links, shares and search results keep working — even when the headline has changed.
* **Anything the update does not mention is left exactly as you had it.** The author, the publication date, the ordering, and any categories or tags the update does not carry are untouched.
* **A post that cannot be found is refused, never replaced.** If the post SearchChamp names has been deleted, is in the trash, or is a different kind of content than expected, the update is refused and nothing at all is written. It never falls back to creating a new post, which is exactly what produced duplicates before.
* **A failed image never costs you a live article.** If an image cannot be downloaded while updating an existing post, that post is left completely alone — still published, with the text it already had — and the update is reported as failed. Nothing is deleted, trashed or unpublished.
* Note for developers: this release is what the SearchChamp platform gates in-place editing on. A connected site keeps refusing in-place edits until it reports version 0.11.0 or later. There is deliberately no 0.10.x — that range is left unused so routine plugin work cannot switch the capability on by accident.

= 0.9.10 =
* **Fixed: articles with a SearchChamp-hosted image still failed to publish, and arrived without the image.** 0.9.9 fixed one half of this; the image host has since changed how it answers the plugin's "does this exist?" question, and the download itself was not prepared for the answer. The plugin used to assume that question had already found the file's final location, and refused to move if the download was pointed somewhere else. It now follows that redirect itself — checking the destination against the same strict list of approved hosts at every step, exactly as before — so these articles publish with their image attached. An article whose image genuinely cannot be fetched is still kept as a draft, never deleted.

= 0.9.9 =
* **Fixed: publishing an article with an image created the post and then deleted it.** Every article carrying an image hosted by SearchChamp failed to publish, and the post that had just been created was permanently removed in the process. The plugin asked the image host for the file's details using a request type that host does not answer, took the refusal as "this image does not exist", and gave up. It now retries the way it downloads, so these articles publish normally.
* **Fixed: an image that cannot be fetched no longer costs you the article.** If an image genuinely cannot be downloaded, the article is now kept as a draft with that image left out, instead of being deleted. Nothing is ever removed permanently — the draft is there to edit, retry or publish by hand.

= 0.9.8 =
* Fixed: generated articles displayed their headline twice — the duplicate heading is now removed rather than just made smaller.
* Fixed: headings kept their ID and CSS class when published, so anchor links and custom styling keep working.

= 0.9.7 =
* Fixed: links whose web address contains round brackets (Wikipedia and Microsoft documentation pages, for example) were published as raw text instead of becoming links.
* Fixed: Fix History offered an Undo button on the record of a previous undo, which could never succeed.
* Fixed: "Reverted" is now available in the status filter — asking for it previously returned every row instead.

= 0.9.6 =
* **Privacy fix: your focus keywords were visible to anyone on the internet.** The focus keyword you set for a post — in the block editor sidebar, the SEO meta box or the Bulk SEO Meta screen — was being published through WordPress's built-in content API on every published post, readable without logging in and listable for the whole site in a single request. It is never shown on the page itself, so this was your keyword strategy leaving the site without you asking it to. It is now visible only to people who can edit that post. Your SEO title and description are still public, as they must be — they are printed into every page's HTML and are meant to be read by search engines.
* **Security fix: the Bulk SEO Meta screen leaked how many hidden posts matched a search.** After the 0.9.5 fix, contributors and authors correctly saw none of other people's unpublished posts — but the *size* of the page still changed depending on how many matched, so someone could type a word into the search box and learn whether an unpublished post about it existed, and roughly how many. Nothing about their contents was ever visible. The screen now filters before it searches, so the page is identical whether hidden posts match or not. Administrators and editors see no change.
* Changed (developers): the safeguard that checks admin screens for permission checks now covers every entry point a logged-in or anonymous visitor can reach — AJAX handlers, admin form handlers, and the `init` and `template_redirect` hooks — not just `admin_init`. Public hooks are held to a different, stricter standard, because a permission check on a hook that anonymous visitors reach proves nothing. No plugin code needed to change to satisfy it; the gap was in the safeguard, not the plugin.

= 0.9.5 =
* **Security fix: the Bulk SEO Meta screen showed unpublished posts to users who should not see them.** The screen is open to anyone who can edit posts, which is deliberate — an author editing their own posts' SEO meta is a normal thing to do. But with "Include drafts & private" ticked it listed **every** draft, pending, private and scheduled post on the site, including other people's, with their SEO title and description in editable fields. A contributor could read an administrator's unpublished content. Saving was never affected: writes were always permission-checked per post, so nobody could ever change meta on a post they could not edit. The list now shows you only the posts you can actually edit. Administrators and editors see no change.
* **Security fix: a background data migration could be triggered by any signed-in user.** A one-time focus-keyword migration ran whenever any logged-in user — including a subscriber — loaded an admin screen, scanning every post on the site. It now runs for administrators only, exactly once, as intended.
* Fixed: the block editor's "not connected" message told you to open **SearchChamp SEO › Settings**. There is no Settings submenu — the API key lives on the SearchChamp SEO screen itself, and the message now says so. (0.9.4 fixed this message's brand name but left the wrong destination.)
* Fixed: an error on the AI Search Readiness screen could show the word "undefined" instead of a readable message when the page's translations had not loaded.
* Fixed: a leftover "Atlio" reference in the Security & Trust page's HTML source.

= 0.9.4 =
* Fixed: the block editor's sidebar still called itself "Atlio SEO", and its "not connected" message told you to open an **Atlio SEO** menu that does not exist — the menu is **SearchChamp SEO**. Both now match the rest of the plugin.
* Added: every label, button and message in the block-editor sidebar can now be translated. Previously all ~27 of them were fixed English that no translation file could reach.
* Changed: the **External services** section now names SearchChamp's image storage host on Amazon S3 (`atlio-cw-assets-*.s3.eu-west-1.amazonaws.com`), which your site downloads article images from, and describes the AI-crawler ingest address precisely. Nothing about what the plugin contacts has changed — only what the readme admits to.
* Fixed (documentation): the readme claimed the Bulk SEO Meta screen limits AI generation to 10 posts per run. It does not, and never did; that cap belongs to the separate bulk action on the Posts and Pages screens. Both are now described accurately.
* Fixed (documentation): the privacy summary said nothing leaves your site until you connect, while the IndexNow section said IndexNow works without a connection. Both were true in part; the summary now states the exception plainly.
* Fixed (documentation): "all off by default" did not hold for the llms.txt file SearchChamp can publish to a connected site. The wording now describes what actually happens; the behaviour is unchanged.
* Changed (developers): three safeguards were widened to cover the classes they were meant to protect, rather than the single instances they were written for — the brand check now reads the plugin's JavaScript as well as its PHP; the external-services check now works backwards from every outbound call and catches hostnames built from a pattern instead of written out in full; and the translation-template check now matches measured `wp i18n make-pot` behaviour across every argument of every translatable string, not just one.

= 0.9.3 =
* Fixed: the "What SearchChamp Manages" screen never loaded the plugin's stylesheet, so its panels rendered unstyled and its "Applied fixes" table pushed the page sideways on a phone. Both are fixed, and the same check now covers every screen in the plugin rather than one at a time.
* Fixed: wide tables on AI Visibility, AI Search Readiness and Bulk SEO Meta now scroll on their own instead of stretching the page sideways on small screens.
* Fixed: the Bulk SEO Meta screen loaded only part of the plugin's styling, which left its table without the scrolling behaviour the other screens have.
* Changed (developers): the translation-template check now flags the code pattern that actually prevents a plural message from being translated. The previous version of the check looked for the wrong pattern, so a broken message could still slip through.
* Changed (developers): the safeguard that stops a saved setting or database key from being renamed by accident — which would lose your data — now also sees keys built at runtime, keys held in a shared property, and keys passed through a variable. Six such keys were previously unchecked.
* Note (developers): PHP 9 will change a default in the built-in CSV writer this plugin is pinned against for its export-format test. The export itself is unaffected; the test comparison will be revisited before PHP 9.

= 0.9.2 =
* Fixed: an exported Fix History CSV file could, in rare cases, be misread by Excel, Google Sheets and LibreOffice — a value containing a backslash immediately followed by a quotation mark could spill into extra columns, and text that spilled over could be treated as a spreadsheet formula. Exports are now written in strict CSV format, so every value stays in its own cell.
* Fixed: the "%d anomaly detected." message on the "What SearchChamp Manages" screen could never be translated, because it was invisible to the translation-file generator. It is now included in the translation template.
* Fixed: the Security & Trust screen no longer scrolls sideways on a phone. Its tables now scroll on their own, and the screen finally loads the plugin's stylesheet like every other screen does.

= 0.9.1 =
* Fixed: the plugin could not reach SearchChamp at all on a fresh install — its built-in service addresses still pointed at hostnames that have been retired. All of them now point at the current ones. If you had entered an address manually, it keeps working and still selects the same environment.
* Fixed: images in published drafts could not be imported once the addresses above were corrected, because the plugin's strict image-source safety list still named only the old hostnames. Both were corrected together.
* Fixed: the "Active robots.txt block" preview on AI Search Readiness was invisible at every screen size, leaving its heading above an empty space.
* Fixed: the Markdown copies of your pages now answer "not changed" to AI crawlers that already have the current version, instead of re-sending the whole page every time.
* Fixed: on narrow screens the Fix History table ran off the side of the page and its Undo buttons could not be reached. The table now scrolls within the page.
* Fixed: the three "Manage in SearchChamp" links opened a dead address.
* Fixed: structured data containing damaged text is now rejected outright rather than being quietly stored with the damaged characters replaced.
* Added: AI Search Readiness now shows exactly which capabilities this copy of the plugin reports to SearchChamp.

= 0.9.0 =
* Changed: the plugin's screens, menus and messages now say "SearchChamp" throughout, matching the plugin's name. Nothing you have saved is affected — your settings, history and links are untouched.
* Fixed: cleared every remaining issue reported by the official WordPress Plugin Check, so the plugin now passes it with zero errors.
* Changed: the wordpress.org build no longer ships the self-update component at all. It was already switched off in this build; now it is simply not included. Direct downloads from searchchamp.com continue to update themselves as before.
* Fixed: the Fix History CSV export is built without opening a temporary file handle.

= 0.8.2 =
* Fixed: when an Undo could not be completed, the plugin reported it as a server error instead of explaining what happened. The real reason — for example that no saved copy of the page is available — is now returned properly.
* Fixed: after undoing a whole batch of internal links at once, **Fix History** kept showing an Undo button for changes that had already been reversed, and clicking it failed. Those entries are now correctly marked as reverted.

= 0.8.1 =
* Fixed: if you had this plugin installed twice — for example the wordpress.org copy alongside an older direct download — activating the second copy could take the whole site down with a fatal error instead of showing the "installed twice" notice. Both copies now coexist safely and the notice appears as intended.

= 0.8.0 =
* Added: your llms.txt now points AI engines at the plain-Markdown copy of every page, and names the full list, so they can read your content without wrestling with your HTML.
* Added: when Markdown copies are switched on, robots.txt explicitly tells the AI crawlers you allow that they may fetch them.
* Added: per-engine access controls for ChatGPT, Perplexity, Claude and Gemini on the **AI Search Readiness** screen — allow, block, or say nothing. If SearchChamp manages your policy, the controls are read-only and explain why, with a link to change it there.
* Added: an AI Search Readiness score out of 100 with a plain-language "next steps" list, so you can see what is missing and what fixing it is worth.
* Added: Markdown coverage — how many of your published pages actually have a Markdown copy.
* Added: AI-crawler visit counts on the same screen, with a clear "showing cached data" note when SearchChamp cannot be reached. Everything read from WordPress keeps working offline.
* Added: the plugin now reports ten more AI crawlers it previously ignored, including Claude's search and user agents, Perplexity's user agent, Amazonbot, CCBot, DuckDuckGo's assistant and Mistral. Ordinary search crawlers such as Googlebot are deliberately not reported, so your reporting budget is spent on AI traffic.
* Added: a warning when your llms.txt has not been refreshed in over a week.
* Fixed: an AI-access policy you set yourself now actually appears in robots.txt, and is shown consistently on the readiness screen and in the self-test. A policy sent by SearchChamp always takes precedence, and your own settings are kept separately so neither overwrites the other.
* Security: an llms.txt sent by SearchChamp is saved exactly as received. The screen also warns you that publishing your own version over a SearchChamp-managed file will be undone automatically.
* Security: the new robots.txt lines are validated so an unusual URL prefix cannot inject extra rules into the file, and changing AI access requires an administrator, a valid form token, and a server-side check that SearchChamp is not managing the policy.

= 0.7.0 =
* Added: your published articles can now carry full structured data (JSON-LD `@graph`) generated by SearchChamp — including the real article author with their credentials and bio, plus FAQ and How-To markup when the article genuinely contains those sections.
* Added: a site with no other SEO plugin installed now gets complete article structured data. Previously it received only a minimal version.
* Added: structured data can be filled in for articles you already published, in batches, without republishing them.
* Added: every structured-data change is recorded on the Fix History screen and can be undone. Undo restores exactly what was there before — the previous version, or nothing at all if there was none.
* Added: the plugin now tells SearchChamp that this build can receive structured data, so the platform can hold off until your site is actually running a version that supports it.
* Fixed: no duplicate or conflicting structured data when Yoast, Rank Math, All in One SEO, or SEOPress is active. The parts those plugins already handle (organisation, site, FAQ, How-To) are left to them; only the richer article and author details are added.
* Fixed: renaming a post or changing your permalink structure no longer breaks the article's structured-data identity — the link is refreshed on every page load while the underlying identifier is preserved.
* Fixed: if you edit an article after its structured data was generated, the FAQ and How-To markup is withheld rather than describing content that is no longer on the page.
* Security: structured data is escaped so that a `</script>` sequence inside any field cannot break out of the script block, and oversized or malformed data is rejected before it is stored.
* Security: structured data is never shown for a draft, private, or trashed post.

= 0.6.0 =
* Added: internal-link deploy (`link_insert`) — Atlio can now add an internal link to an existing published page by wrapping text that is already on the page, without changing anything else about it. Every other byte of the page, including its block structure, is left exactly as it was.
* Added: one-click undo for an added link, from **Atlio SEO → Fix History**. The page is restored to its exact previous version. If you edited the page after Atlio added the link, the undo is refused rather than overwriting your newer edit.
* Added: links are never added inside an existing link, inside a code or preformatted block, or on a page built with a page builder (Elementor, Bricks, Divi and similar).
* Added: only links to pages on your own site are accepted. Links to other domains, and anything that is not a normal `http`/`https` address, are rejected.
* Added: if the page changed since the suggestion was made and the text can no longer be located exactly, that link is reported as drifted and skipped — Atlio never guesses where it should go.
* Note: this is the WordPress side of the feature. The Atlio platform does not yet send these requests for published pages, so nothing changes on your site until that is switched on.

= 0.5.0 =
* Fixed: SEO title and description are now written through All in One SEO's own API. AIOSEO stores per-post SEO in its own table rather than post meta, so previous writes silently never reached the rendered page. If AIOSEO's API is unavailable the field is now reported as unsupported instead of reporting a success that never took effect.
* Fixed: citation markers such as `[Source: https://example.com/]` no longer leak into the meta description on any supported SEO plugin (Yoast, Rank Math, All in One SEO, SEOPress, or none).
* Fixed: published articles no longer carry a duplicate `<h1>`. The body heading that restated the post title is demoted to `<h2>`, so a page has exactly one `<h1>`.
* Fixed: literal citation markers and leftover markdown links in an article body are now rendered as real, clickable links instead of appearing as raw text.
* Added: citation links carry `rel="nofollow noopener"` and an `atlio-citation` CSS class for styling. Only `http` and `https` links are ever created; text inside existing links, `<code>` and `<pre>` is left untouched.
* Added: `atlio_seo_preserve_body_h1` option/filter for headless themes that do not render the post title and therefore need the body `<h1>` preserved.
* Renamed to **SearchChamp SEO** — the product this plugin connects to is now called SearchChamp.
  The text domain moves to `searchchamp-seo`. Your settings, SEO meta, connection, and fix history
  are stored under unchanged keys and carry over; an existing `atlio-seo-<locale>.mo` translation
  still resolves.
* The wordpress.org build no longer performs its own update checks — wordpress.org handles updates
  for it. The self-hosted build is unchanged and still verifies every package against a published
  SHA-256 before installing it.
* Security: structured data (JSON-LD) is now encoded so that a `</script>` sequence inside a post
  title or site name can no longer break out of the script block.
* Hardening pass for the plugin directory review: unslashed and unvalidated superglobal reads
  tightened across the admin screens, pagination markup escaped, the client IP used for rate
  limiting is now validated as an IP, and diagnostic logging to the PHP error log is gated behind
  `WP_DEBUG` (the durable records in Security & Trust and AI Search Readiness are unchanged).
* Installing this plugin twice (for example the directory build alongside an older direct
  download) now shows an admin notice instead of causing a fatal error.

= 0.4.3 =
* Fixed a duplicate `<title>` and canonical tag on sites with no other SEO plugin.
* Signed-receiver hardening: rate limiting keyed on the client IP, IP-change tracking moved after
  signature verification, and an atomic claim so concurrent replays of a fix apply exactly once.
* Crash-safe publishing: a failed image import no longer leaves a half-published post or a
  permanently stuck 409.
* The Gutenberg sidebar no longer writes an empty value over existing Yoast / Rank Math meta.
* Undo no longer clears a field when the recorded previous value was empty.
* The block converter keeps content on unbalanced tags and builds valid nested lists.
* CSV export escapes spreadsheet formula injection.
* Uninstall cleans up post and user meta, and iterates every site on multisite.
* Bulk meta generation is capped at 10 posts per run, with a notice.
  *(Corrected in 0.9.4: this cap was only ever added to the bulk action on the Posts and Pages
  screens. The Bulk SEO Meta screen has no such cap and never had one — see the feature list
  above for what each one actually does.)*
* The settings page no longer blocks for ~30 seconds on a slow connection.


= 0.4.2 =
* Rebuilt the connect/settings screen: a wizard-style connect flow with a live connection-health
  card showing the site, workspace, connected project, plugin version, and the real last heartbeat
  time.
* The Default Project dropdown auto-selects the project bound to this site.
* The raw API URL moved behind an **Advanced settings** disclosure that defaults to the production
  gateway.
* The "not connected" admin notice is dismissible per user.
* The connect/settings, AI Visibility, and AI Search Readiness screens load their JavaScript and
  CSS through the standard enqueue APIs, and every interactive control carries a test id.
  *(Corrected in 0.5.0: the original note claimed this for all admin screens. The post-editor
  panel, the classic SEO meta box, and the bulk meta editor still render small inline blocks.)*
* Shipped a real `.pot` file and `languages/` directory, fixed a broken arrow glyph in the
  IndexNow label, and synced the readme Stable tag to the plugin version.

= 0.4.1 =
* Explicit **Reconnect / Rotate key** control — the only action that rotates the signing secret, so
  an ordinary settings save no longer invalidates in-flight signed requests.
* **Disconnect** control that clears local credentials and revokes the connection server-side.
* A dismissible admin notice when the API returns HTTP 401, instead of raw "API error 401" text in
  widgets.
* Connection state is read live from the platform rather than from a cached option, and a daily
  heartbeat reports real liveness.

= 0.4.0 =
* Publish drafts from the platform into WordPress: creates the post, imports hero and inline images
  into the media library, converts the body to native blocks, sets the featured image, and writes
  SEO meta to the active SEO plugin. Idempotent and replay-protected.
* Signed schema-config endpoint for the `sameAs` / Wikidata entity data.

= 0.3.0 =
* llms.txt publishing endpoint.
* Per-route rate limiting on the plugin's REST namespace.
* Security alerting when a signed request's source IP changes unexpectedly.

= 0.1.0 =
* Initial release: settings page, API connection validation, SEO meta box.

== Upgrade Notice ==

= 0.5.0 =
Renamed to SearchChamp SEO. Your settings, SEO meta, and fix history carry over unchanged. Includes
a structured-data escaping fix and a directory-compliance hardening pass.

= 0.4.3 =
Fixes duplicate title/canonical tags, hardens the signed receivers against replay, and makes draft
publishing crash-safe.

= 0.4.2 =
Rebuilt connect screen with a live connection-health card and packaging fixes.
