=== 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.0
Stable tag: 4.21.460
Requires PHP: 8.0
License: GPLv2 or later
License URI: https://www.gnu.org/licenses/gpl-2.0.html

Free Yoast alternative with AI. Bring your own Claude or Gemini key (free), or subscribe for $10/month - cancel anytime.

== Description ==

**Stop paying $99/year for Yoast Premium.** CloudScale SEO AI Optimizer 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. The plugin itself charges nothing. Setup is install, paste key, click Generate Missing.

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

It covers the technical SEO WordPress leaves out - sitemaps, robots.txt, structured data, OpenGraph, canonical URLs - and adds an AI toolkit that bulk-generates meta descriptions, ALT text, summaries, FAQ schema and related article links. A built-in **Site Audit** scores your setup across 20+ checks with **one-click quick fixes**.

= 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 (Managed Subscription) =

* Turns any post into a spoken MP3 with one click (Generate Audio, for logged-in editors only); 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 400,000 narration characters per month, roughly 7 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 (size-adjust, ascent-override, descent-override) 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%
* Refresh rebuilds the health cache; it also auto-rebuilds after any bulk AI run, and counters show posts needing an AI run and queued pipeline jobs

= Dashboard Integration =

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

= 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
* Reload re-analyses everything; a per-row button redoes one post. 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
* Sorted by verdict then confidence; elapsed-time counter and Stop button

= 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

= 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

= What This Plugin Does Not Do =

* No third-party SEO data, keyword research databases or rank tracking
* No hidden upsells, no tracking, no lock-in - BYOK is free forever and the subscription cancels anytime with no data loss

== 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, https://api.cloudscale.consulting/v1/tts
**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 Paystack, 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.
**API key:** Your managed licence key (see key-handling note above).

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

= Paystack =

**Service:** Paystack Payments Africa (Pty) Ltd, a Stripe company - https://paystack.com
**What it is:** Payment processor for the Managed Subscription.
**Data sent:** Your email address and payment details, entered by you directly on Paystack's own hosted checkout page. The plugin never transmits or stores card data.
**When data is sent:** On Subscribe, when you are redirected to Paystack's checkout. Cancellations and refunds are handled there too.

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

= 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

= 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 ==

= 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-4-6 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.

== 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.460 =
* Change: The monthly narration allowance on the managed plan rises again, from 300,000 to 400,000 characters, roughly 7 hours of audio. Existing subscriptions are raised automatically with nothing to do at your end. As before this is funded by the low cost voice model: 400,000 characters costs about $0.25 a month to produce, against a plan that bills $10.

= 4.21.458 =
* Change: The narrator accent picker is now Default (US) and British, the two accents the narration voices genuinely have. Irish has been removed alongside Australian and South African: those accents only ever worked by asking a much more expensive model to imitate them, and the imitation was the reason narrations sometimes drifted mid-article. A post with a removed accent saved falls back to whichever accent your site is currently set to, or Default (US) if none is set. Every narration now comes from the same voice model, so audio is consistent from post to post.
* Change: The narration prompt now explains that leaving it at the default keeps you on the fast, low cost voice model, and that writing your own moves narrations to a costlier model that reads instructions, using up the monthly allowance faster. The progress screen names the model used either way.

= 4.21.453 =
* Change: The narrator accent picker now offers Default (US), British and Irish. The Australian and South African options have been removed. A post that had one of them saved falls back to Default (US) automatically; nothing else about narration changes.

= 4.21.452 =
* Change: The monthly narration allowance on the managed plan rises from 200,000 to 300,000 characters, roughly 5.5 hours of audio. Existing subscriptions are raised automatically, with no action needed.
* New: Narration is now produced by a lower cost speech model when that model can deliver exactly what you asked for, which is what pays for the larger allowance. Your accent choice, chosen voice and speaking speed are all preserved. Requests the cheaper model cannot serve faithfully, meaning the Irish, Australian and South African accents or a narration prompt you have customised, continue to use the original model instead of quietly losing the setting.
* New: The narration progress screen, the "narration ready" confirmation and the settings page voice preview now all name the exact speech model and voice used, so you can always tell what you are listening to.
* fix(models): installs that had saved the corrupted "Claude Sonnet 4 (stable pinned)" id are now repaired automatically. Fixing the dropdown in 4.21.446 stopped anyone new picking it, but anyone who already had it kept getting API errors on every call, because model ids are passed to the provider without an allowlist so a custom model can be typed in. The existing model-migration map now rewrites the broken id to claude-sonnet-4-20250514 on the next options read.
* feat(build): the build now rejects malformed AI model ids. Anthropic ids never contain a dot, so a version string spliced in by a bump script is detectable — which is exactly how the id above was corrupted. Migration-map keys are exempt, since repairing an install requires naming the broken id.
* fix(models): the "Claude Sonnet 4 (stable pinned)" option sent a malformed model id. A version-bump script had rewritten claude-sonnet-4-20250514 into claude-sonnet-4.20.140514, and because model ids are passed through without an allowlist, choosing that option produced an API error rather than a pinned model. Restored from git history; the current build script uses anchored replacements and cannot corrupt it again.
* fix: the readme Description was over WordPress.org's limit (3022 words against 2500) and the live plugin page was truncating the external-services disclosure mid-sentence. WordPress.org counts words, not characters, and folds unrecognised sections such as External Services into the Description before trimming. Prose condensed to 2193 words with every service, endpoint and legal URL retained. The build now blocks on this check.
* Fix: The narrator accent picker (British, Irish, Australian, South African) now genuinely changes the accent of generated audio. Previously the accent request was sent in a way the speech model usually ignored, so narrations stayed American.
* Fix: Voice previews on the settings page now play in your selected accent instead of always sounding American.
* Change: Audio narration is now billed by the actual characters read aloud against a monthly pool of 200,000 characters, with no per-post limit. Narrate many short posts or a few long ones until the characters add up - short posts no longer each cost a whole "credit". The $5 Narration Boost now adds 400,000 characters. Usage meters show characters instead of credits.
* Fix: The "Optimise Click Through Rate" tool no longer strips the article's primary keyword from the title (e.g. it now keeps "Embeddings in AI" instead of replacing it with a metaphor). The writer must preserve the core search term and the scorer now penalises titles that drop it.
* Fix: CTR scores are shown honestly as a potential score out of 100 (an AI estimate for comparing versions), not as a fake click-through percentage.

= 4.21.393 =
* New: Animated progress screen while a narration generates (audio equalizer, live "audio X of N" progress bar) with a note that you can leave the page - it keeps running on the server.
* New: Managed subscribers see how many narrations they have left this month during generation (e.g. "29 of 30 left"); bring-your-own-key users see "Using your own OpenAI key". The count comes back on the generation response, with no extra API call.
* New: If audio can't be generated you get a clear on-screen message explaining what to do (enable the managed subscription or add an OpenAI key in Settings), instead of a silent failure.

= 4.21.391 =
* Change: Audio narration now runs entirely on the server as a background job. Click Generate Audio once and you can close or background the browser (or lock your phone) - it keeps generating and finishes on its own. Returning to the post shows the finished player. This fixes failures on long articles and when the browser was backgrounded on mobile.

= 4.21.388 =
* New: The Generate Audio voice menu now groups voices into Female and Male (plus Neutral), so you can quickly pick a male or female narrator. Every voice remains selectable.
* New: A success popup confirms when a narration is ready and stays on screen for 10 seconds (with a "View it now" button) before refreshing the post.

= 4.21.387 =
* Change: Audio narration is now managed-first with a fallback to your own key. If your managed subscription is enabled, narrations use the managed service (with the included 30/month allowance); if a managed call fails and you also have your own OpenAI key, it falls back to that so narration never gets blocked. Sites without the managed subscription keep using their own OpenAI key.

= 4.21.386 =
* Change: Audio narration now uses your existing OpenAI key directly (the same key the plugin suite already uses for image generation) with no proxy and no monthly quota. The managed service is only a fallback when no OpenAI key is present. You no longer need to configure a separate key on the managed server.

= 4.21.385 =
* New: Per-post narrator voice - clicking Generate Audio opens a menu of all 11 voices (the post's current/default highlighted); the chosen voice narrates that post and is remembered on it, so different posts can use different voices. The Settings voice is the site-wide default.
* Fix: The "Optimise Click Through Rate" button now always shows a clear error message on failure instead of ever failing silently.
* Fix: The Generate Audio and Optimise Click Through Rate button scripts are now delivered inline, fixing intermittent cases where the buttons appeared but did nothing.

= 4.21.377 =
* New: Audio Narration voice picker - choose from eleven narrator voices (Alloy, Ash, Ballad, Cedar, Coral, Fable, Marin, Nova, Onyx, Sage, Verse), each with its own character, replacing the previous gender and accent selectors
* New: Free voice preview - a Preview Voice button in settings speaks a test sentence in the selected voice using your narration prompt, without using any narration credits
* New: $5 Narration Boost - one-time purchase of 30 extra narration credits that roll over until used; offered in settings and automatically when the monthly limit is reached

= 4.21.375 =
* New: Audio Narration (Managed Subscription) - a Generate Audio button on each post converts the article into a spoken MP3 via the managed text-to-speech service and shows a "Listen to this article" player directly under the Related Articles block
* New: Audio Narration settings card (AI Content tab) - male or female narrator voice, English, American, or Irish accent, and an editable narration prompt that shapes tone and pacing
* New: Code blocks are never read aloud - each one is replaced with a short spoken note that a code block is available in the article
* New: Managed Subscription includes 30 article narrations per month; regenerating a post replaces its previous MP3 instead of piling up media library files

= 4.21.371 =
* Docs: Documented the "noindex RSS feeds" setting - feature list entry, FAQ explaining when to leave it enabled (almost always) and that RSS readers are unaffected, plus 4.21.368 changelog notes

= 4.21.340 =
* Fix: Site Audit's "Fix -> DevTools" button on the Security Headers card linked to the wrong admin page slug (cloudscale-devtools instead of cloudscale-cyber-devtools), landing on "Sorry, you are not allowed to access this page."
* Fix: Root cause of "empty AI response" failures found - Claude Sonnet 5 was exhausting its whole token budget on internal reasoning ("thinking") before ever producing an answer at a 300/400 max_tokens ceiling, plus a separate bug where the response parser only ever read content block 0, silently dropping the answer when a thinking block came first. Raised every AI call's max_tokens to 1024 and fixed the parser to scan all content blocks for the real text/thinking blocks
* Fix: call_ai_generate_desc() (used by the scheduled daily batch) had no retry at all on empty AI response, unlike its sibling functions - added one retry attempt
* Fix: AI Summary and Title Optimiser now retry with a simpler prompt on empty/invalid AI responses, matching the meta description fallback - fixes "Invalid summary response from AI" pipeline failures
* Fix: Auto Pipeline meta description step occasionally failing with "Empty description in AI response" - falls back to a simpler prompt before giving up
* Fix: Pipeline failure Telegram alerts now include exception class, file/line, and a short stack trace for steps not resolved by the automatic retry
* Fix: Empty AI completions now also covered by the existing 60-second retry (previously only network errors), since testing showed it is a same-window API blip rather than an independent unlucky roll on each prompt
* Add: Rolling diagnostic log of empty AI completions (stop_reason, token counts) included in Telegram alerts so failures can be diagnosed without live reproduction
* Fix: Managed-proxy subscription status check was a blocking ~300ms HTTP call inline on every settings page load once the billing reset date passed; moved to a daily WP-Cron job so it never blocks page render
* Fix: Canonical URLs collapsed every query-string/pagination variant of a page to its bare URL, including /page/2/ with genuinely different content - now self-references the current pagination/query state instead of telling search engines to ignore that content

= 4.21.368 =
* Add: "noindex RSS feeds" setting (Features & Robots, on by default) - sends an X-Robots-Tag noindex header on all /feed/ URLs so they stop wasting crawl budget and disappear from Search Console's "Crawled - currently not indexed" report; feeds keep working for RSS readers
* Fix: Canonical URLs now always strip widget-pagination (prp_page_*, cspv_rp_*) and comment-moderation (unapproved, moderation-hash) query parameters; the "Strip UTM params" setting governs only UTM and click-ID tracking parameters, as its label says
* Fix: Default robots.txt widget-parameter patterns now match the parameter anywhere in the query string, not just first position

= 4.21.109 =
* Add: Schema column in AI Meta Description Writer table - shows ✓/✗ per-post JSON-LD schema coverage; sortable
* Add: First-run welcome banner - guides new installs through getting an API key with direct links to Anthropic and Gemini
* Add: FAQ schema auto-generation in Auto Pipeline - every new post publish generates FAQPage JSON-LD automatically
* Add: SEO Site Audit section added to help documentation
* Change: WordPress.org tags updated to free yoast alternative, ai seo, claude ai, seo audit, schema generator
* Change: readme.txt short description and opening paragraph rewritten to lead with the value proposition
* Fix: Help docs table of contents numbering - replaced CSS columns layout with two separate ordered lists

= 4.20.93 =
* feat: Add "Generate Missing Titles" button - batch-generates SEO title tags for posts with no _cs_seo_title set
* feat: Add "Have Title Tag" counter card in the summary row alongside "Have Description"

= 4.20.67 =
* Add: Broken Link Checker - Date Created column in results table; Post, Date Created, and Status headers are now sortable
* Fix: Broken Link Checker - sites returning 503 to server-side requests (Cloudflare JS-challenge) now treated as alive, eliminating false positives
* Add: Redirects - Created column moved next to Last hit and made sortable; Hits, Last hit, and Created all support click-to-sort
* Fix: Broken Link Checker - sites returning 401 to server-side requests (Reuters, WatchMojo) now treated as alive, eliminating false positives

= 4.20.27 =
* Security: Broken Link Checker SSRF guard - `blc_is_ssrf_blocked()` now rejects URLs that resolve to loopback, link-local, or private IP ranges before making the `wp_remote_head()` call
* Fix: Title Optimiser stale detection - added 60-second grace period so applying a title never immediately flags the post as "Edited since analysis" due to `wp_update_post()` timing
* Fix: Title Optimiser applied-post hint text corrected from "re-analyse to compare originals" to "Title was changed to suggested title"
* Docs: Added Readability, Broken Link Checker, and Image SEO Audit feature sections to readme description
* Docs: Corrected "What This Plugin Does Not Do" - removed incorrect "no readability scoring" claim; added BLC to External Services section

= 4.20.2 =
* Add: Title Optimiser - new 🎯 Title Optimiser tab; AI suggests keyword-rich replacement titles for all published posts; shows before/after SEO score and identified keywords; apply individually or in bulk; applying a title auto-creates a 301 redirect from the old URL
* Change: AI Summary Box now uses SEO-first prompts - primary keyword front-loaded in every field, secondary keywords woven in naturally, written for search intent; existing summaries unchanged until regenerated

= 4.19.142 =
* Add: Readability scoring - pure-PHP analysis of sentence length, heading density, and passive-voice rate; no AI call required; scores 0–100 with Easy / Moderate / Hard labels
* Add: Readability badge in post metabox - colour-coded score with sub-metrics (avg words/sentence, words per heading, passive voice %); Score button for on-demand rescoring; auto-refreshes after meta description generation
* Add: Sortable Readability column in Meta Writer post list - badge updates live during pipeline run
* Add: Auto-pipeline and save_post hook both trigger readability scoring to keep scores fresh
* Add: Migrate Categories panel in the Categories tab - lists all categories sorted by post count (fewest first) and lets you migrate posts category-by-category
* Add: Single-category posts require a swap target; multi-category posts can be removed or swapped per row
* Add: Apply per-row or Apply All to batch-migrate every pending post in one click
* Add: Delete Category button appears automatically once a category is empty - available in both the category list (for already-empty categories) and in the migration view once all posts are migrated; server-side guard prevents deletion if posts still exist
* Fix: Settings save safety net - new option keys no longer silently drop on save

Releases before 4.19.142 are listed in CHANGELOG.md, included in the plugin directory. WordPress.org truncates this section past 5000 words, so only recent releases are repeated here.

== Upgrade Notice ==

= 4.17.5 =
Code quality pass: DocBlocks on all trait files, settings page i18n coverage expanded.

= 4.17.4 =
PCP compliance pass - resolves all WordPress.org Plugin Check errors and warnings.

= 4.17.3 =
Settings page now stays on the current tab after saving.

= 4.17.2 =
Fix: Generate & Sync now correctly trims link counts downward in all cases.

= 4.17.1 =
Generate Missing and Sync Counts merged into one Generate & Sync button.

= 4.17.0 =
Related Articles table post links now open the live post for previewing related links.

= 4.16.9 =
Fix: Related Articles table post links now open the correct post editor.

= 4.16.8 =
Fix: table no longer blanks out when Generate Missing finds nothing to process.

= 4.16.7 =
Sync Counts can now fill additional slots when you increase the link count setting.

= 4.16.6 =
Fix: Related Articles link count settings now save correctly.

= 4.16.5 =
New: Sync Counts button instantly normalises all Related Articles link counts to match your current settings.

= 4.16.4 =
Fix: Generate Missing now correctly finds pending posts regardless of which filter the table is currently showing.

= 4.16.3 =
Fix: Related Articles batch now correctly updates row counts live and reloads with the right filter.

= 4.16.2 =
Fix: Refresh Stale batch now reliably finds and processes posts.

= 4.16.1 =
Fix: batch operations now read directly from the visible table rows, resolving persistent "No posts to process" issue.

= 4.16.0 =
Fix: Refresh Stale now correctly finds and reprocesses completed posts.

= 4.15.9 =
Refresh Stale batch rewritten - more robust page-by-page processing, fixes "No posts to process" in certain environments.

= 4.15.8 =
Bug fix: Refresh Stale now genuinely regenerates all posts with current settings.

= 4.15.7 =
Bug fix: Related Articles batch now processes all posts, not just the first 50.

= 4.15.6 =
New: manually edit any post's meta description inline from the AI Tools table.

= 4.15.5 =
Code quality and i18n improvements; no functional changes.

= 4.15.4 =
PCP compliance fix: JSON-LD schema output now uses WordPress API, eliminating the critical echoed script tag violation.

= 4.15.3 =
Bug fix: eliminates PHP warning from batch scheduler log display.

= 4.15.2 =
Scoring progress now shows post count (e.g. "Post 23 of 186") in the status bar.

= 4.15.1 =
Generate Missing now reliably scores unscored posts in a second phase with its own fresh post fetch.

= 4.15.0 =
Generate Missing now also calculates SEO scores for any unscored posts in a second pass.

= 4.14.9 =
All data columns in the AI Tools post table are now sortable by clicking the header.

= 4.14.8 =
Bug fix: homepage SEO score now persists across page reloads.

= 4.14.7 =
AI Tools post table now has a Date column and sortable headers for Post, Date, and SEO Score.

= 4.14.6 =
Swapped Categories and Scheduled Batch tab order in settings page.

= 4.0.0 – 4.14.6 =
For earlier upgrade notices and full version history see CHANGELOG.md in the plugin directory.
