=== CloudScale SEO AI Optimizer ===
Contributors: andrewjbaker
Tags: seo plugin, ai seo, claude ai, seo audit, schema generator
Requires at least: 6.0
Tested up to: 7.1
Stable tag: 4.21.631
Requires PHP: 8.0
License: GPLv2 or later
License URI: https://www.gnu.org/licenses/gpl-2.0.html

Automatic AI SEO for your whole site - meta, ALT text, links, schema and audio narration on publish. Free BYOK or $10/month.

== Description ==

**Most WordPress sites lose search traffic to SEO work nobody has time to do.** Meta descriptions go unwritten, images ship with no ALT text, and related posts never get linked. Yoast and RankMath give you the fields; you still fill in every one yourself.

**CloudScale SEO AI Optimizer fills them in automatically.** On publish, the AI Auto Pipeline writes the meta description, scores the SEO, generates ALT text, builds an on-page summary, and links related articles. One-click **Audio Narration** turns any post into a spoken article in 11 AI voices. It also runs the technical SEO WordPress skips underneath, and a built-in **Site Audit** scores your setup across 20+ checks with one-click fixes.

It replaces Yoast Premium and RankMath Pro, in two plans:

**Bring Your Own API Key - free, forever.** Paste your own Anthropic Claude or Google Gemini key (2 minutes at console.anthropic.com or aistudio.google.com) and pay the provider directly: about $0.50-$1.50 per 200 posts, under $0.10 per 100 on Claude Haiku, or nothing on Gemini's free tier. Setup is install, paste key, click Generate Missing.

**Managed Subscription - $10 per month.** No API key required, AI usage and audio narration included, cancel anytime.

= Core SEO Features =

* Custom meta title and description per post and page; canonical URL on every page
* OpenGraph (title, description, image, type, locale) and Twitter/X Card tags
* JSON-LD: Person (author pages), Article/BlogPosting (posts), WebSite (homepage), Breadcrumb
* Configurable site name, locale, Twitter handle, default OG image
* XML sitemap (index + child sitemaps) with configurable post types and taxonomies, plus a plain-text /sitemap.txt for AI crawlers
* robots.txt editor with AI bot blocking (GPTBot, CCBot, Claude-Web, anthropic-ai and others), plus llms.txt support
* noindex controls for search results, 404s, attachments, author archives, tag archives and RSS feeds
* UTM stripping in canonical URLs; widget-pagination and comment-moderation parameters always stripped

= AI Auto Pipeline =

* Runs every AI operation in a background process the moment a post is published - no WP-Cron dependency, and optionally again whenever a published post is saved
* Per publish: meta description, SEO score, focus keyword, ALT text for all images, AI-suggested internal links, AI summary box and Related Articles
* Gutenberg-safe block-level link injection; classic editor falls back to str_replace
* 50-word minimum content guard; HMAC-authenticated async request with a 120-second TTL
* "Re-run AI Automation" button in the metabox with live log output

= AI Meta Writer =

* Claude or Gemini, with a model selector: Automatic (always the current recommended model), Claude 3.5/3.7 Sonnet, Claude Haiku, Gemini 2.0 Flash, Gemini 1.5 Pro, or a custom model string
* Generate meta descriptions per post or in bulk; fix descriptions that are too short or long, and titles outside the optimal 50-60 characters
* Inline edit button on each row for manual corrections
* Configurable character range injected into the prompt, auto-retry when the AI overshoots, backoff on HTTP 429
* Fully editable system prompt with reset to default; Test Key verifies your key
* Sortable post table (title, date, SEO score, description length, title length, ALT status), timestamped progress log, Stop button
* Scheduled batch generation via WP Cron

= AI SEO Scoring =

* AI rates each post 0-100 with a one-sentence strengths or weaknesses note
* Calculate SEO Scores runs a bulk pass; Generate Missing scores any post still lacking one
* Stored in post meta (_cs_seo_score, _cs_seo_notes) and survive deactivation

= Title Optimiser =

* AI suggests SEO-optimised replacement titles for every published post, with a before/after score (0-100)
* Identifies the primary and secondary keywords the article is about
* One-click Apply updates title and slug and 301-redirects the old URL; "Apply All Suggested" does the lot with confirmation
* Sort by date or most-commented; suggestions stored in post meta so you can apply selectively

= AI Summary Box =

* AI summary box prepended to post content: What it is, Why it matters, Key takeaway
* Written SEO-first - primary keyword front-loaded, secondary keywords woven in, optimised for search intent
* Also written into Article JSON-LD as description, abstract and disambiguatingDescription
* Bulk generation with progress tracking, stop button and paginated list; force regenerate overwrites existing summaries
* Collapsible card; a global show/hide toggle keeps generated content

= ALT Text Generator =

* Audits all post images for missing ALT text and generates it with article context for relevance
* Configurable article excerpt length sent to the AI (100-2000 characters)
* Bulk generation with progress tracking; Show All reveals images that already have ALT text

= Related Articles =

* Injects related post links at the top and bottom of every post, block-safe in Gutenberg and classic posts
* AI-scored candidate pool across the full library; configurable counts (2-5 top, 3-10 bottom) with independent toggles
* Generate Missing processes new posts; Refresh Stale re-runs changed content; Sync Counts trims or fills to new count settings
* Post Status table shows per-post state (pending, complete, failed) with filter tabs

= Audio Narration =

* Turns any post into a spoken MP3 with one click (Generate Audio, for logged-in editors only), using your own OpenAI key or included on the Managed Subscription; a "Listen to this article" player appears under Related Articles
* Eleven narrator voices, from Cedar (calm, authoritative) to Marin (conversational) to Onyx (deep broadcast)
* Free voice previews, which never draw on your narration allowance
* Editable narration prompt controls tone and pacing; defaults to a calm tech-essay read
* Code blocks are never read aloud; each becomes a short spoken note instead
* MP3s land in your media library; regenerating replaces the previous narration
* Includes 500,000 narration characters per month, roughly 9 hours of audio, billed by the actual characters read aloud so there is no per-post limit; a $5 Narration Boost adds a further 400,000 characters that roll over until used

= Performance Features =

* font-display: swap to kill Flash of Invisible Text, plus font metric overrides to reduce Cumulative Layout Shift
* Defer font CSS via the media="print" swap technique; auto-download CDN (Google) fonts locally for speed and GDPR compliance
* Font CSS scanner with Auto-Fix All, backup and undo
* Defer render-blocking JavaScript with configurable exclusions
* HTML, CSS and JS minification (5-15 percent smaller pages)
* HTTPS mixed content scanner and one-click fixer across posts, pages, metadata, options and comments

= SEO Health Dashboard =

* Dashboard widget with five coverage pillars - Posts (meta), SEO (score), Images (ALT), Links (related articles), Summaries - as colour-coded pills: green >= 90%, amber >= 60%, red < 60%
* Rebuilds automatically after any bulk AI run

= Dashboard Integration =

* Post editor metabox and Gutenberg sidebar panel (CloudScale Meta Boxes): custom title, description, OG image, AI summary fields and one-click generation

= Category Fixer =

* Suggests better category assignments using Claude on each post's title, slug, tags and AI summary against your category list
* Up to four categories per post, only ones that already exist, never Uncategorized
* Colour-coded review table (green additions, red removals, grey kept) with a confidence badge
* Per-post Apply and Skip, bulk Apply All Changed, filters: All, Changed, Unchanged, Low Confidence, Missing
* Nothing changes until you click Apply

= Category Health and Drift Detection =

* Post counts per category with a pass/fail coverage indicator
* Drift Detection flags categories that have drifted from their original focus or become catch-all buckets, each with a verdict (drifting or catch-all), confidence score and AI reasoning

= Readability Analyser =

* Pure-PHP scoring, no AI call required: 0-100 with an Easy / Moderate / Hard label from sentence length, heading density and passive-voice rate
* Colour-coded metabox badge with sub-metrics (words per sentence, words per heading, passive voice percentage)
* Sortable Readability column; recalculated on save and after each Auto Pipeline run

= Humanize =

* Scores how human a post reads, 0 to 100: half measured on your server with no AI call, half a blind AI second opinion, every signal shown
* A mild-to-heavy slider rewrites prose by paragraph without touching facts, links, headings, quotes, code or tables, previewing every change

= Broken Link Checker =

* Finds outbound links in posts and pages that return HTTP 4xx/5xx or fail to connect
* Server-side HEAD request per URL, deduplicated so each external URL is checked once
* Results table with post title, anchor text, URL and status. SSRF-safe: link-local, loopback and private IP ranges are blocked server-side

= Image SEO Audit =

* Flags Media Library SEO issues: missing ALT text, camera-default filenames (IMG_001, DSC_0045, screenshot2) and oversized files over 500 KB, sorted by issue count with thumbnails and direct edit links

== External Services ==

The plugin contacts the services below, only when you enable and use the relevant feature.

**Notes that apply to every service listed here:**

* **Key handling:** any API key, bot token or licence key you enter is stored only in your own WordPress database (wp_options) and is never transmitted anywhere except directly to that service's own host.
* **"Post content" means:** the post title, up to 6,000 characters of the post body, and the system prompt you configured.
* No visitor data, personal data, analytics or tracking data is ever transmitted.
* Nothing is sent from your front end. Calls come from a WP Admin action, a WP Cron batch you scheduled, or the Auto Pipeline on publish/update if enabled.

= Anthropic Claude API =

**Service:** Anthropic PBC - https://anthropic.com
**Endpoint:** https://api.anthropic.com/v1/messages
**Data sent:** Post content (see note above).
**When data is sent:** On Generate, Fix or Generate with Claude; on a scheduled batch for posts without a meta description; or on publish/update via Auto Pipeline.
**API key:** Your own Anthropic key (see key-handling note above).

Privacy Policy: https://www.anthropic.com/privacy
Terms of Service: https://www.anthropic.com/terms
API documentation: https://docs.anthropic.com

= Google Gemini API =

**Service:** Google LLC - https://ai.google.dev
**Endpoint:** https://generativelanguage.googleapis.com/v1beta/models/
**Data sent:** Post content (see note above).
**When data is sent:** Same triggers as Anthropic above, when Gemini is your selected provider.
**API key:** Your own Google AI key (see key-handling note above).

Privacy Policy: https://policies.google.com/privacy
Terms of Service: https://policies.google.com/terms
API documentation: https://ai.google.dev/docs

= Google Fonts CDN =

**Service:** Google LLC - https://fonts.googleapis.com, https://fonts.gstatic.com
**Data sent:** Only the URL of a Google Font stylesheet already registered on your site.
**When data is sent:** Only on "Download Fonts" (Performance tab), which copies those font files to your server so they are served locally instead of from Google's CDN - better GDPR compliance and Core Web Vitals.
**API key:** None required.

Privacy Policy: https://policies.google.com/privacy
Terms of Service: https://policies.google.com/terms

= CloudScale Managed API =

**Service:** CloudScale Consulting (Pty) Ltd - https://cloudscale.consulting
**Endpoints:** https://api.cloudscale.consulting/v1/messages and https://api.cloudscale.consulting/v1/tts for the AI features; and for the subscription itself https://api.cloudscale.consulting/checkout, /status, /cancel, /refund-request, /recover, /recover/verify and /contact
**What it is:** Optional paid alternative to your own key; provides Claude (text) and OpenAI text-to-speech (Audio Narration) on our account. Billed via PayPal, below.
**Data sent:** Post content (see note above) for text features; for Audio Narration, the full cleaned article text with code blocks removed, plus your narration prompt. Your managed licence key accompanies every call.
**When data is sent:** On any AI feature or Generate Audio with the Managed API enabled; on subscribe, status check or cancel; and once, non-blocking, if you delete the plugin while a subscription is active, so it is cancelled for you. Leave us a message sends your email, message, hostname and licence key to /contact, only on Send.
**API key:** Your managed licence key (see key-handling note above).

Privacy Policy: https://cloudscale.consulting/privacy
Terms of Service: https://cloudscale.consulting/terms

= OpenAI (optional, Audio Narration with your own key) =

**Service:** OpenAI, L.L.C. - openai.com
**Endpoint:** https://api.openai.com/v1/audio/speech
**What it is:** Text-to-speech for Audio Narration. Contacted DIRECTLY only when you supply your own OpenAI key; if you use the Managed API instead, the call is made by CloudScale on its account and your site never contacts OpenAI.
**Data sent:** The cleaned article text to be narrated, with code blocks removed, plus the chosen voice and your own OpenAI key.
**When data is sent:** Only when you click Generate Audio with an OpenAI key configured and the Managed API off.
**API key:** Your own OpenAI key.

Privacy Policy: openai.com/policies/privacy-policy
Terms of Service: openai.com/policies/row-terms-of-use

= Telegram Bot API =

**Service:** Telegram Messenger Inc. - https://telegram.org
**Endpoint:** https://api.telegram.org/bot{token}/sendMessage
**What it is:** Optional real-time error alerts to a Telegram chat you own.
**Data sent:** An error summary only - the PHP error message, file name and line number, and the related WordPress post ID or URL.
**When data is sent:** Only when a plugin error is caught and you have set a bot token and chat ID in Notifications settings. Nothing is sent if it is unconfigured.
**API key:** Your own bot token (see key-handling note above).

Privacy Policy: https://telegram.org/privacy
Terms of Service: https://telegram.org/tos
API documentation: https://core.telegram.org/bots/api

= PayPal (optional, Managed AI subscription checkout) =

**Service:** PayPal Holdings, Inc. - https://developer.paypal.com
**Endpoints:** https://api-m.paypal.com/ to create a subscription and check its status (https://api-m.sandbox.paypal.com/ in sandbox mode), plus https://www.paypal.com/sdk/js, PayPal's browser SDK, loaded into this plugin's admin screen from PayPal's own origin.
**Data sent:** Subscriber email address and subscription amount. Card entry happens on PayPal's hosted checkout; no card data reaches this plugin.
**When data is sent:** Only when an administrator opens or checks a Managed AI subscription.

Privacy Policy: https://www.paypal.com/us/legalhub/paypal/privacy-full
Terms of Service: https://www.paypal.com/us/legalhub/paypal/useragreement-full

= Broken Link Checker (server-side URL probing) =

**What it is:** Your server sends an HTTP HEAD request to each unique outbound URL in your own published posts and pages, to check its status.
**Data sent:** The URL only, with standard HTTP headers (a User-Agent identifying your site). No post content, no user data.
**When data is sent:** Only while you run a scan from the Broken Link Checker tab. No automatic or scheduled scanning.
**Note:** `sslverify` is disabled so sites with expired or self-signed certificates can still be checked; loopback, link-local and private IP ranges are blocked. These are third-party sites you linked to yourself, so no single terms of service applies.

== Installation ==

1. Upload the plugin zip via Plugins > Add New > Upload Plugin, or extract to wp-content/plugins/cloudscale-seo-ai-optimizer/
2. Activate the plugin through the Plugins menu
3. Go to Settings > CloudScale SEO to configure your site name, OG image, and Person schema
4. To use the AI Meta Writer, go to the Optimise SEO tab and enter your API key for either Anthropic Claude or Google Gemini
5. Click Test Key to verify your key, then Load Posts to see your site description status
6. Use Generate Missing to create descriptions for posts that have none, or Fix Descriptions to correct any that are outside your configured character range
7. Visit the Performance tab to enable font optimization, JavaScript deferral, and HTML minification

**Important:** Deactivate any other SEO plugins (Yoast, RankMath, All in One SEO) before using this plugin to avoid duplicate meta tags in your page output.

== Frequently Asked Questions ==

= What does this plugin deliberately not do? =

No third-party SEO data, keyword research databases or rank tracking. No hidden upsells, no tracking and no lock-in: bring-your-own-key is free forever, and the optional subscription cancels at any time with no data loss.

= Do I need an API key? =

Only if you want to use the AI Meta Writer or ALT Text Generator features. The core SEO functionality (canonical URLs, OpenGraph, Twitter Cards, structured data, custom titles and descriptions) works without any API key.

= Which AI provider should I choose? =

Both work well. Anthropic Claude tends to produce slightly more nuanced descriptions. Google Gemini Flash is extremely fast and cost effective for large sites. You can switch providers at any time without losing any generated content.

= How much does it cost? =

**Bring Your Own API Key (BYOK) - free.** You supply your own Anthropic Claude or Google Gemini API key and pay the AI provider directly. Generating descriptions for a typical 200-post blog costs approximately $1.20-$1.50 using Claude Sonnet, or under $0.50 using Gemini Flash. Claude Haiku is under $0.10 per 100 posts. Google Gemini has a free tier suitable for most personal blogs.

**Managed Subscription - $10 per month.** Your AI usage is included; no API key needed. Cancel anytime - your generated content stays in WordPress post meta after cancellation.

= Should I leave "noindex RSS feeds" enabled? =

Yes, for almost every site. WordPress publishes an RSS feed for every post, category, and comment thread, and search engines crawl all of them even though they are just XML copies of your content - they waste crawl budget and show up in Google Search Console as "Crawled - currently not indexed". This setting sends a noindex header on feed URLs while leaving the feeds fully working for RSS readers, podcast apps, and feed directories (none of those rely on Google indexing the feed URL). Only disable it if you have a specific reason to want your raw feed URLs appearing in Google search results.

= Will this conflict with Yoast or RankMath? =

Yes. Running two SEO plugins simultaneously produces duplicate meta tags. Deactivate your existing SEO plugin before activating this one.

= Where are my meta descriptions stored? =

In WordPress post meta, using the key _cs_seo_desc. Titles use _cs_seo_title. OG images use _cs_seo_ogimg. These are standard post meta fields that persist if you deactivate the plugin.

= Can I use a custom system prompt? =

Yes. The AI Meta Writer section includes a fully editable system prompt. The character range you configure in the min/max fields is injected automatically at call time.

= What happens if the generated description is the wrong length? =

The plugin measures the returned description before saving it. If it is outside your configured range, it automatically sends a correction request to the AI with the exact character count and direction. The corrected version is what gets saved.

= How does font display optimization work? =

The plugin scans your theme and plugin CSS files for @font-face rules and injects font-display: swap to prevent fonts from blocking page rendering. It also adds metric overrides to reduce layout shift when fonts load. All changes create a backup that you can undo with one click. Enable these features in the Performance tab.

= How does JavaScript deferral work? =

Adding the defer attribute to script tags allows them to download in parallel with HTML parsing and execute only after the document is ready. jQuery and other commonly problematic scripts are excluded automatically. You can add additional exclusions by script handle or URL substring.

= Can I schedule automatic description generation? =

Yes. The Scheduled Batch tab lets you select which days of the week to run automatic generation. The batch runs at midnight server time and only processes posts that do not yet have a meta description. It never overwrites existing ones.

= What does Auto Pipeline do? =

Auto Pipeline fires a background process the moment a post is published. It runs every AI step in sequence: meta description, SEO score, focus keyword, ALT text for all post images, AI-suggested internal links, AI summary box, and Related Articles. The process runs in a separate PHP request so it does not slow down the publish action. You can also trigger a re-run manually from the post metabox using the "Re-run AI Automation" button.

= What is the Automatic model option? =

Selecting Automatic tells the plugin to always use the current recommended model for your chosen provider (currently claude-sonnet-5 for Anthropic and gemini-2.0-flash for Google). When Anthropic or Google release a better default model the plugin will automatically switch without any settings change on your part. Users who have already pinned a specific model are not affected.

= How does Related Articles work? =

The plugin scores all posts against each other using shared categories, tags, and content signals, then stores a ranked candidate pool per post. The top two to five links appear above the post content and three to ten appear below. You control the counts from the Related Articles settings. The Generate Missing button processes unscored posts; Sync Counts adjusts existing results to match updated count settings without running the full pipeline again.

= How does the Humanness score work, and what can Humanize change? =

* A Humanize button on every post (logged-in editors only) scores how human the writing reads: 100 reads as written, 0 reads as generated, a blend lands in between
* Half the score is measured on your server with no AI call: sentence length variety, stock AI phrasing, hedging density, sentence opener variety, paragraph length variety, personal voice and rule-of-three habit
* The other half is a blind AI second opinion, shown the prose and nothing else, so it cannot score the provenance instead of the writing. It runs only when you press the button for it, which states its cost first, so opening the dialog and reading the score spends nothing
* Every sub-signal is shown with its raw measurement, so the number says what to edit rather than only that something is wrong
* Works without an API key or subscription: the local half still scores, and the dialog says the second opinion is missing
* Code blocks, quotes, tables and shortcodes are excluded, so an article quoting a stack trace is not judged on its cadence
* Scores are stored against a fingerprint of the prose, so an edited post shows as unscored rather than showing a stale number
* A mild-to-heavy slider then rewrites the prose: level 1 only swaps inflated words for plain ones and keeps every sentence, level 5 rewrites each paragraph from its facts and its point
* No level may change a fact, number, name, date or quotation, invent anything, or restructure beyond the paragraph
* Headings are never rewritten, because WordPress builds in-page anchors from them; quotes, code and tables are left alone too
* Every rewritten paragraph is checked before it can be used: identical link targets, identical inline markup, identical shortcodes and a sane length, or that paragraph is kept as it was
* In the block editor the rewrite is applied WITHOUT saving, so you read it in place and save or undo; on the front end Apply writes to the post
* Shows every change before anything is applied, says what a run will cost before it starts, and can be stopped between batches
* On the Managed AI plan Humanize has its own allowance of 500,000 characters a month and uses none of your AI actions; the dialog shows what is used, what is left and what this article needs, and a run that would not fit is not started. With your own API key there is no allowance, only your provider's charges
* In the editor the score is measured from the text on screen, unsaved changes included, with no AI call and no action spent, and it is re-measured after a rewrite so you can see whether the number moved

== Screenshots ==

1. SEO Settings tab showing site identity, OG tags, and schema configuration
2. AI Tools tab with Auto Pipeline card, provider selection, API key, and model chooser
3. Generate Descriptions panel with summary cards, bulk action buttons, and live progress log
4. Post table showing per-post description, title, SEO score, and ALT status badges
5. Post editor metabox with custom title, description field, and Re-run AI Automation button
6. Performance tab with font optimization, JavaScript deferral, and minification settings
7. ALT Text Generator with image audit table and bulk generation
8. Related Articles Post Status table with pipeline state filters and Sync Counts button
9. Category Fixer review table with colour-coded category pills and Apply/Skip controls
10. Dashboard widget showing SEO health pills with colour-coded coverage scores

== Changelog ==

= 4.21.631 =
* Fixed: Sentence highlighting in the read-along player works again on narrated posts. A text change had broken the script, so the audio played with no highlighting.
* Fixed: The AI prompts that forbid dashes name the real em and en dash again, instead of forbidding a plain hyphen.
* Fixed: The Humanize Apply button no longer looks disabled after a run. The "Now" column is labelled "Current" in both the bar and the editor.
* Fixed: A Humanize rewrite batch interrupted by a server restart is retried once instead of showing "Unexpected token".
* Fixed: ALT text generation only marks the images that were actually generated, and reports the ones that failed.
* Fixed: Related Articles "Generate & Sync" reports posts that failed instead of saying "Done".
* Fixed: Find Dead Destinations counts destinations it could not check, instead of reporting them as alive.
* Fixed: The HowTo quick fix shows the AI's own error message when it fails.
* Fixed: Low contrast score badges and amber labels are darker so their white text is readable.
* Changed: Leave us a message on the licence card now reaches CloudScale, instead of your own Telegram bot.
* Security: The PayPal return pages verify a nonce before reading anything. Outbound licence and Telegram requests send their own User-Agent rather than your site address.

= 4.21.620 =
* Changed: The AI second opinion in the Humanize score is now on demand. Opening the dialog shows the half measured on your server straight away and spends nothing. A "Get the AI second opinion" button adds the other half, and tells you what it will use before you press it. It used to be fetched automatically the first time you opened the dialog on a post. A result you have already paid for is still shown for free whenever you come back.

= 4.21.619 =
* Fixed: The score bands under the Humanness score read "70 to 100", "45 to 69" and "0 to 44". They were written with dashes.

= 4.21.618 =
* Fixed: The Humanize allowance line now appears the first time you open the dialog. It used to stay hidden until your first rewrite, because that was the first moment the plugin learned your allowance.

= 4.21.617 =
* Added: On the Managed AI plan, Humanize now has its own monthly allowance of 500,000 characters, the same size as the narration allowance. A Humanize score or rewrite draws from it and no longer uses any of your AI actions.
* Added: The Humanize dialog and the editor panel show how much of the allowance is used and left, and how many characters the article in front of you needs, before you start. The figures update after every batch. A boost is counted inside the total, so used plus left always equals the figure shown.
* Added: A rewrite that would not fit in what is left is not started, so an article is never left half rewritten and nothing is spent on it. If you have your own API key configured, Humanize carries on using that instead.
* Added: The licence card has a Humanize characters meter next to AI actions and narration.

= 4.21.616 =
* Changed: The line in the Generate Audio menu that says whether the article has changed since its narration now stands out: a pastel green box when the narration is current or its history is not known, pastel amber when the article really has changed. The stray question mark in front of it is gone.

= 4.21.615 =
* Changed: The Humanize Apply button is now on screen for the whole run, above the list of changes, showing how many paragraphs are ready. It used to stay hidden until the last batch finished, below a list that scrolls, so it looked as if there was no way to apply anything. If a batch fails part way, the paragraphs from the batches that finished can still be applied.
* Changed: A paragraph that Humanize keeps as it is now shows in green, since your text being left untouched is the safe outcome, and the reason reads as a sentence.

= 4.21.614 =
* Fixed: Humanize could fail a whole batch with "Invalid humanize response from AI" when the AI returned a reply that was almost, but not quite, valid. The usual cause was a link inside a paragraph. The good paragraphs in that reply are now recovered and used, a reply that was cut short never offers its unfinished paragraph, and the message you see when nothing can be recovered is readable and no longer quotes unrelated earlier calls.
* Fixed: Humanize was discarding nearly every paragraph that contained a link with a hyphen in its address, reporting that the link target had changed. The dash cleanup was reaching inside the link. It now cleans the words and leaves links, tags and shortcodes exactly as they were.
* Changed: A Humanize rewrite never contains an em dash or an en dash, and the AI is told to use hyphens sparingly. A word you hyphenate on purpose is left as you wrote it.
* Changed: The second opinion note in the Humanize dialog has dashes removed like all other AI text, and the dialog copy no longer uses hyphenated words.

= 4.21.613 =
* Added: Generate Audio now tells you whether the article has changed since its narration was generated. The voice menu and the replace prompt say "current", "changed since <date>" or, for a narration made before this version, "unknown", and the pill label carries the same answer at a glance. It compares what would be read aloud, so an edit to a code block, a reference list or markup that is never narrated does not count as a change.

= 4.21.612 =
* Fixed: Humanize never rewrites a quotation. Paragraphs inside a quote block, or inside a blockquote in the classic editor, were being offered for rewriting even though the dialog said quotes are left alone.
* Fixed: A rewritten paragraph is now refused if any HTML attribute changed, not only the link target, so a rewrite cannot add or alter markup attributes.
* Fixed: In the block editor, Apply skips any paragraph you edited or removed while the rewrite was running, instead of replacing what you just typed. Closing the panel during a run now stops it cleanly, and if a batch fails you can still apply the batches that finished.
* Added: Humanize AI calls are limited per user per hour, so one account cannot run up the AI allowance in a loop.
* Added: In the block editor, the Humanize panel also scores the text you are looking at, unsaved changes included, with no AI call and nothing spent, and re-scores after a rewrite so you can see whether the number moved.
* Added: A mild-to-heavy slider on the same dialog rewrites the prose to read less like AI. It works a paragraph at a time and never touches headings, quotes, code or tables. No level may change a fact, a number, a name or a quotation, or invent anything. Every rewritten paragraph is checked for identical links, identical inline markup, identical shortcodes and a sane length before it can be used, and anything that fails is kept as it was. You see every change first, and you are told how many AI actions a run will cost before it starts. In the block editor the rewrite is applied without saving, so you read it in place and then save it or undo it; on the front end you press Apply and it writes to the post.
* Added: A Humanize button on each post scores how human the writing reads, out of 100. Half the score is measured on your own server from the habits generated prose has and yours does not (sentence length variety, stock phrasing, hedging, opener variety, paragraph variety, personal voice, lists of three), and half is a second opinion from the AI, which is shown the text and nothing else. Every signal is listed with what was actually measured, so the score points at something you can edit. It works with no API key, showing the local half and saying so. It is an estimate for your own editing, not a detector: no reliable AI detector exists, and this does not claim to be one.

= 4.21.609 =
* Fixed: Clicking Generate Audio on a post with unsaved changes lost those changes. The narration is made from the saved version of the post, so it was also narrating the old text, and the page reload at the end of it discarded whatever you had just written. Generate Audio now asks you to save first and offers to do it for you, then opens the voice menu. If the save fails, nothing is narrated and no allowance is spent.

= 4.21.597 =
* ADDED: The Generate Audio button is now in the block editor sidebar as well, directly beneath Generate Post Image with AI in the featured image box. Narration previously had to be started from the published post on the front end; now you can do it while you are still writing. It is the same button and the same flow, voice menu, character cost and all, just reachable from where you actually are. It appears once the saved post has at least 120 characters of narratable text, the same rule the front-end button follows.

= 4.21.591 =
* Fixed: Audio narration spelled ordinary capitalised words out letter by letter. "WHERE" was read as "W.H.E.R.E", and a database article writes WHERE, JOIN, GROUP and ORDER in capitals on nearly every line; the query keywords, the HTTP verbs and a much wider set of short English words are now read as words. Backticks around inline code become a pause instead of reaching the voice.
* Added: Headings are announced ("Heading" or "Sub heading"), their section number is read on its own with a pause after it, and they are narrated at three quarters of the chosen speaking speed so a heading no longer sounds like the first sentence of the section before it.

= 4.21.566 =
* Changed: CloudScale SEO credit link (opt-in, off by default, in Related Articles settings) hover colour changed to gold.

= 4.21.557 to 4.21.565 =
* Added and refined the opt-in CloudScale SEO credit link across several releases: added a help-doc link, moved it from an admin-only panel into the public "Listen to this article" audio player (its intended, visible location), made it an off-by-default setting for WordPress.org compliance, and fixed a blurry hover effect (electric blue, lift on hover, no scale/glow artefacts). Full details in CHANGELOG.md.
* Added: Hover tooltips on the audio player's play/pause, mute, share and download buttons, tracking each button's current state.
* Fixed: readme.txt short description was 155 characters, over WordPress.org's 150-character limit; trimmed to 124.

= 4.21.554 =
* Changed: Rewrote the Description and tagline to lead with the problem this plugin solves (SEO work nobody has time to do, automated on publish) rather than price, and to mention Audio Narration for the first time. Corrected the Audio Narration section heading, which implied Managed Subscription only.

= 4.21.553 =
* Fixed: Generate Missing's AI retry chain (up to six calls per post: initial, malformed-JSON retry, empty-description fallback, three length-correction passes) could exceed the host's execution-time limit if enough retries ran slow, returning the server's raw error page instead of JSON. The chain is now bounded to a 55-second wall-clock deadline, and a Telegram alert fires on failure.
* Fixed: Batch Run History showed the raw stored timestamp instead of a formatted date.
* Fixed: AI usage/cost tracking mislabelled several prompt types (site-audit prompts tagged as title-optimiser, related-articles prompts tagged as summary/auto-pipeline, and some retry prompts untagged), causing spend to roll up under the wrong feature.
* Fixed: External Services disclosure named Paystack as a live payment processor for the Managed Subscription; every payment call site is hardcoded to PayPal, so the disclosure now names PayPal only.
* Fixed: The "Automatic model" FAQ named claude-sonnet-4-6 as the current Anthropic default; corrected to claude-sonnet-5, matching recommended_model().

= 4.21.548 =
* Fixed: Checking "Enable automatic scheduled batch" left the day checkboxes greyed out until the page was reloaded - the live-toggle listener referenced the wrong element id.
* Fixed: The Title Optimiser widget's progress line never updated, unlike every other AI widget - the status element was missing from its markup.
* Removed: Several dead/orphaned code paths (an unused AI Summary post-list toggle, an unused Category Health/Fixer "Hide Posts" toggle, and an unused legacy PayPal subscribe form superseded by the shared licence card) that referenced page elements that don't exist and were never reachable from any button.

= 4.21.547 =
* Fixed: Background title-analysis batches could get permanently stuck ("0 of N processed") with no error shown, if a single AI call ran long. The batch used to process every queued post in one long-lived request; a slow call could be cut off by the server's own connection timeout before the plugin's own error handling ever ran, silently dropping that post and leaving the whole batch wedged with nothing left to resume it. It now processes one post per background tick and reschedules itself after each one, so a single slow call can no longer strand the rest of the batch.
* Fixed: The "Enable it" link in the Auto Pipeline banner did nothing when clicked - it pointed at an anchor with no matching target on the page.
* Added: The dashboard widget now shows Auto Pipeline status (active/disabled) alongside Batch status.
* Fixed: Clicking the Critical/Warning count in the dashboard's Site Audit summary now jumps straight to that finding and highlights it, instead of leaving you to find it in the full results table.

= 4.21.542 =
* Fixed: Capitalised abbreviations are read as letters instead of as words. AWS is narrated as A.W.S and API as A.P.I, on every voice: the text handed to the narrator is rewritten, so this does not depend on the narration prompt, which the fast managed voice model does not read.
* Note: Left as written, because spelling them out would be the worse reading: abbreviations pronounced as words (NASA, JSON, REST), a word capitalised for emphasis ("Do NOT enable this"), Roman numerals (Part III), anything carrying a digit (EC2, S3) and an ALL CAPS HEADING.
* Note: Editing the narration prompt still moves your narrations onto a dearer, instruction-following voice model. A prompt this plugin shipped as its default no longer counts as an edit, so sites holding an older default keep the cheap model.

= 4.21.540 =
* New: Regenerate links button under a related links block, for a post whose links are there but out of date. The buttons added in 4.21.539 only appear where a block has nothing in it at all.
* New: Change any related link for an article you pick. Each link is listed under its block with a Change button that searches your published articles by title. Your choice is badged as yours and holds its place through later regenerations, until you set that slot back to Automatic.
* Note: The panel and its buttons are visible to admins only. An article the other block on the same post already links is refused with a reason, because the two blocks never show the same article and accepting it would have dropped it from one of them silently.

= 4.21.539 =
* New: Generate buttons on the post itself, for the summary, the AEO answer and the related links. Each appears where its block would be, visible to admins only, so a post the automation never covered can be filled in from the page you are reading. Until now a block with no content rendered nothing at all, which meant that with the Auto Pipeline switched off there was nothing to click anywhere on the post.
* New: Regenerate Links button in the CloudScale SEO Auto Run panel on the post editor, alongside the summary and AEO buttons already there.
* Note: The related links block above an article needs 2 eligible posts and the one below it needs 3. Where a post has fewer, the button now tells you that instead of appearing to do nothing.

= 4.21.522 =
* Fix: The Organization schema no longer claims your personal social profiles as its own, and no longer uses your headshot as a company logo. It was reusing the Person schema's sameAs list, which tells search engines that your personal LinkedIn profile identifies a company. That competes with your own Person schema for the same URL, so neither identity resolves cleanly.
* New: Organization Schema card, with its own name, URL, logo and sameAs fields. Name and URL still fall back to your employer details if you leave them blank, so existing sites are unaffected.
* New: Profile page setting in the Person Schema card. Pick the page about you, usually About, and it is marked as a ProfilePage with you as its subject. This is the strongest signal available for tying a site to a named person, and an About page has none of it by default.
* New: A second employer can be named. If you have a day job and also run your own practice, naming both is what lets an AI model join the identity it already knows from press coverage to the one it learns from your site. Naming only one is the usual reason an AI answer will claim that a known executive has no blog.
* Fix: A post's publisher logo was also your headshot rather than a company logo, and it is taken from the new Organization logo field now.
* Fix: The Site Audit no longer warns that a post's publisher is missing when it is correctly present. It checked for a property that a reference does not carry, and now resolves the reference against the Organization on the page.
* Change: The schema nodes now reference each other. WebSite says who authors and publishes it, a post's publisher points at your Organization instead of a separate copy built from your site name, and the page about you links back to your Person entry. Previously each node stood alone and nothing stated that the site was written by the person described in it.

= 4.21.518 =
* Change: Follow along is no longer a switch. Each sentence is highlighted as the narration reads it, from the moment you press play, and there is nothing to turn on. The button that used to sit under the player is gone, along with the line reporting how many sentences it matched, which was a number only the author of the article could act on.
* Change: The author's own line under the player now says only whether the timings were measured, and offers a button only when a measurement did not run or did not work. A narration that has been timed shows a fact and asks nothing.

= 4.21.517 =
* New: Narration now rests for about a second after a title and between paragraphs. It used to read a heading straight into the sentence that followed, with no gap.
* Note: The rest is real silence inserted into the audio, because it cannot be asked for. The speech service accepts no pause markup and caps its own pause well under a second whatever it is told: measured on a heading followed by a sentence, the plugin's current text produces 660ms, the best of five wordings produces 810ms, and adding "leave a full second of silence" to the narration prompt produces 690ms.
* Note: Narrations made before this release keep the pacing they were made with. Regenerate an article to hear the rests.

= 4.21.515 =
* Change: Follow along is now timed from the audio itself rather than estimated from the text. After a narration is made, your browser reads the finished audio once, finds where the voice actually starts each sentence, and stores those times with the article. Readers get the measured times and pay nothing for them.
* Change: The highlight no longer moves early, because it no longer has to. The small lead existed to cover a boundary that was estimated and could be late as easily as early; a measured boundary needs no allowance.
* Change: Existing narrations are timed too. Open a narrated post as an editor and the measurement runs once, with no re narration and no credits spent. There is a button on the player to run it again.
* Fix: The narration menu now opens on the voice you last used, not on the site default. Speed and accent were already remembered across articles and the voice was not, so every new article reverted to the default however many times in a row you had chosen something else.
* Fix: A speed or accent choice that fails to save now says so and puts the control back to what is stored. It used to keep the new value on screen and quietly keep the old one on the server, which is what made narration look as though it forgot your choices.

= 4.21.499 =
* New: Follow along. Each sentence of the article is highlighted as the narration reads it, and clicking a sentence jumps the audio to that point. Off until you switch it on, and then remembered.
* Note: The highlight is anchored to points measured in the audio itself, with the sentences in between apportioned by length, because no speech service reports word timings. It can therefore run slightly early or late in a long passage and corrects itself at the next anchor.
* Note: The article text is never rewritten. Matching happens in your browser, a sentence that cannot be matched is left unhighlighted rather than guessed at, and the player says how many it matched. Code blocks and tables, which are not narrated, are never highlighted.
* Note: Narrations made before this release carry no timings, so the button appears once an article has been narrated again.

= 4.21.492 =
* Change: Narration is several times faster and is split into far fewer pieces. The same 21,000 character article went from 107 pieces to 6, completing in about 11 seconds. Pieces are now produced four at a time instead of one after another, each piece can be four times larger, and narration reaches the voice service directly rather than through a routing layer that was failing to place roughly one request in five.
* Change: The progress dialog now shows an estimated time remaining, measured from the job's own pace rather than assumed.
* Change: The progress dialog now tells you how many parts came from your managed plan and how many from your own OpenAI key, and keeps saying so. It used to report only the most recent part, so it alternated between the two every few seconds, which read as your plan failing when it was working normally.
* Change: When a part cannot be produced on your managed plan, the dialog now says why: the voice service did not respond, your monthly allowance is used up, your licence was not accepted, or the service was rate limiting.
* Fix: The dialog showed a generic model name instead of the voice tier, and the same underlying mistake had disabled the setting that keeps one article on a single voice, so a mid article switch could change the narrator without saying so.
* Fix: The dialog named the voice service's internal id rather than the voice you chose, so picking Marin could report a name you had never been offered.
* Fix: A part delivered twice could be counted twice in the progress figures.
* Fix: Uninstalling now removes the narration working data left behind by a narration interrupted part way.
* Change: Requests to the managed service now identify the plugin, version and feature making them, so usage can be attributed to the feature that caused it. If you use the managed subscription, this release is required.

= Before 4.21.492 =
Every earlier release, back to 4.0.0, is in CHANGELOG.md, which ships in the plugin directory. The readme keeps the last year of releases only, because WordPress.org silently truncates this section past 5,000 words, from the oldest entry.
