{"id":285903,"date":"2026-03-17T21:08:18","date_gmt":"2026-03-17T21:08:18","guid":{"rendered":"https:\/\/en-za.wordpress.org\/plugins\/cloudscale-seo-ai-optimizer\/"},"modified":"2026-04-09T20:08:31","modified_gmt":"2026-04-09T20:08:31","slug":"cloudscale-seo-ai-optimizer","status":"publish","type":"plugin","link":"https:\/\/hat.wordpress.org\/plugins\/cloudscale-seo-ai-optimizer\/","author":23459506,"comment_status":"closed","ping_status":"closed","template":"","meta":{"_crdt_document":"","version":"4.20.64","stable_tag":"4.20.64","tested":"6.9.4","requires":"6.0","requires_php":"8.0","requires_plugins":null,"header_name":"CloudScale SEO AI Optimizer","header_author":"Andrew Baker","header_description":"Lightweight SEO with AI meta descriptions via Claude API. Titles, canonicals, OpenGraph, Twitter Cards, JSON-LD schema, sitemaps, robots.txt, and font display optimization.","assets_banners_color":"","last_updated":"2026-04-09 20:08:31","external_support_url":"","external_repository_url":"","donate_link":"","header_plugin_uri":"https:\/\/andrewbaker.ninja\/2026\/02\/24\/cloudscale-seo-ai-optimiser-enterprise-grade-wordpress-seo-completely-free\/","header_author_uri":"https:\/\/andrewbaker.ninja\/","rating":0,"author_block_rating":0,"active_installs":0,"downloads":905,"num_ratings":0,"support_threads":0,"support_threads_resolved":0,"author_block_count":0,"sections":["description","installation","faq","changelog"],"tags":{"4.19.132":{"tag":"4.19.132","author":"andrewjbaker","date":"2026-04-06 21:00:38"},"4.19.139":{"tag":"4.19.139","author":"andrewjbaker","date":"2026-04-06 22:05:05"},"4.19.140":{"tag":"4.19.140","author":"andrewjbaker","date":"2026-04-06 22:24:17"},"4.19.30":{"tag":"4.19.30","author":"andrewjbaker","date":"2026-03-18 17:36:51"},"4.19.31":{"tag":"4.19.31","author":"andrewjbaker","date":"2026-03-18 17:36:51"},"4.19.33":{"tag":"4.19.33","author":"andrewjbaker","date":"2026-03-18 17:36:51"},"4.19.35":{"tag":"4.19.35","author":"andrewjbaker","date":"2026-03-18 19:40:33"},"4.19.37":{"tag":"4.19.37","author":"andrewjbaker","date":"2026-03-19 08:02:27"},"4.19.38":{"tag":"4.19.38","author":"andrewjbaker","date":"2026-03-19 08:13:31"},"4.19.40":{"tag":"4.19.40","author":"andrewjbaker","date":"2026-03-19 09:03:26"},"4.19.41":{"tag":"4.19.41","author":"andrewjbaker","date":"2026-03-19 10:43:03"},"4.19.51":{"tag":"4.19.51","author":"andrewjbaker","date":"2026-03-21 20:47:49"},"4.19.56":{"tag":"4.19.56","author":"andrewjbaker","date":"2026-03-21 21:51:40"},"4.19.64":{"tag":"4.19.64","author":"andrewjbaker","date":"2026-03-23 09:30:29"},"4.19.65":{"tag":"4.19.65","author":"andrewjbaker","date":"2026-03-23 10:36:29"},"4.19.67":{"tag":"4.19.67","author":"andrewjbaker","date":"2026-03-25 13:43:09"},"4.19.71":{"tag":"4.19.71","author":"andrewjbaker","date":"2026-03-28 14:14:27"},"4.19.72":{"tag":"4.19.72","author":"andrewjbaker","date":"2026-03-28 14:20:25"},"4.19.93":{"tag":"4.19.93","author":"andrewjbaker","date":"2026-03-30 12:08:11"},"4.20.27":{"tag":"4.20.27","author":"andrewjbaker","date":"2026-04-08 19:28:56"},"4.20.64":{"tag":"4.20.64","author":"andrewjbaker","date":"2026-04-09 20:08:31"}},"upgrade_notice":{"4.17.5":"<p>Code quality pass: DocBlocks on all trait files, settings page i18n coverage expanded.<\/p>","4.17.4":"<p>PCP compliance pass \u2014 resolves all WordPress.org Plugin Check errors and warnings.<\/p>","4.17.3":"<p>Settings page now stays on the current tab after saving.<\/p>","4.17.2":"<p>Fix: Generate &amp; Sync now correctly trims link counts downward in all cases.<\/p>","4.17.1":"<p>Generate Missing and Sync Counts merged into one Generate &amp; Sync button.<\/p>","4.17.0":"<p>Related Articles table post links now open the live post for previewing related links.<\/p>","4.16.9":"<p>Fix: Related Articles table post links now open the correct post editor.<\/p>","4.16.8":"<p>Fix: table no longer blanks out when Generate Missing finds nothing to process.<\/p>","4.16.7":"<p>Sync Counts can now fill additional slots when you increase the link count setting.<\/p>","4.16.6":"<p>Fix: Related Articles link count settings now save correctly.<\/p>","4.16.5":"<p>New: Sync Counts button instantly normalises all Related Articles link counts to match your current settings.<\/p>","4.16.4":"<p>Fix: Generate Missing now correctly finds pending posts regardless of which filter the table is currently showing.<\/p>","4.16.3":"<p>Fix: Related Articles batch now correctly updates row counts live and reloads with the right filter.<\/p>","4.16.2":"<p>Fix: Refresh Stale batch now reliably finds and processes posts.<\/p>","4.16.1":"<p>Fix: batch operations now read directly from the visible table rows, resolving persistent &quot;No posts to process&quot; issue.<\/p>","4.16.0":"<p>Fix: Refresh Stale now correctly finds and reprocesses completed posts.<\/p>","4.15.9":"<p>Refresh Stale batch rewritten \u2014 more robust page-by-page processing, fixes &quot;No posts to process&quot; in certain environments.<\/p>","4.15.8":"<p>Bug fix: Refresh Stale now genuinely regenerates all posts with current settings.<\/p>","4.15.7":"<p>Bug fix: Related Articles batch now processes all posts, not just the first 50.<\/p>","4.15.6":"<p>New: manually edit any post&#039;s meta description inline from the AI Tools table.<\/p>","4.15.5":"<p>Code quality and i18n improvements; no functional changes.<\/p>","4.15.4":"<p>PCP compliance fix: JSON-LD schema output now uses WordPress API, eliminating the critical echoed script tag violation.<\/p>","4.15.3":"<p>Bug fix: eliminates PHP warning from batch scheduler log display.<\/p>","4.15.2":"<p>Scoring progress now shows post count (e.g. &quot;Post 23 of 186&quot;) in the status bar.<\/p>","4.15.1":"<p>Generate Missing now reliably scores unscored posts in a second phase with its own fresh post fetch.<\/p>","4.15.0":"<p>Generate Missing now also calculates SEO scores for any unscored posts in a second pass.<\/p>","4.14.9":"<p>All data columns in the AI Tools post table are now sortable by clicking the header.<\/p>","4.14.8":"<p>Bug fix: homepage SEO score now persists across page reloads.<\/p>","4.14.7":"<p>AI Tools post table now has a Date column and sortable headers for Post, Date, and SEO Score.<\/p>","4.14.6":"<p>Swapped Categories and Scheduled Batch tab order in settings page.<\/p>","4.0.0 \u2013 4.14.6":"<p>For earlier upgrade notices and full version history see CHANGELOG.md in the plugin directory.<\/p>"},"ratings":[],"assets_icons":{"icon-128x128.png":{"filename":"icon-128x128.png","revision":3485118,"resolution":"128x128","location":"assets","locale":""},"icon-256x256.png":{"filename":"icon-256x256.png","revision":3485118,"resolution":"256x256","location":"assets","locale":""}},"assets_banners":[],"assets_blueprints":{},"all_blocks":[],"tagged_versions":["4.19.132","4.19.139","4.19.140","4.19.30","4.19.31","4.19.33","4.19.35","4.19.37","4.19.38","4.19.40","4.19.41","4.19.51","4.19.56","4.19.64","4.19.65","4.19.67","4.19.71","4.19.72","4.19.93","4.20.27","4.20.64"],"block_files":[],"assets_screenshots":[],"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"},"jetpack_post_was_ever_published":false},"plugin_section":[],"plugin_tags":[2353,5777,2989,1117,186],"plugin_category":[55],"plugin_contributors":[258042],"plugin_business_model":[],"class_list":["post-285903","plugin","type-plugin","status-publish","hentry","plugin_tags-ai","plugin_tags-meta-description","plugin_tags-opengraph","plugin_tags-schema","plugin_tags-seo","plugin_category-seo-and-marketing","plugin_contributors-andrewjbaker","plugin_committers-andrewjbaker"],"banners":[],"icons":{"svg":false,"icon":"https:\/\/ps.w.org\/cloudscale-seo-ai-optimizer\/assets\/icon-128x128.png?rev=3485118","icon_2x":"https:\/\/ps.w.org\/cloudscale-seo-ai-optimizer\/assets\/icon-256x256.png?rev=3485118","generated":false},"screenshots":[],"raw_content":"<!--section=description-->\n<p>CloudScale SEO AI Optimizer is a completely free SEO plugin built for technical bloggers and site owners who want full control without the overhead of Yoast or RankMath. There is no Pro version, no upsells, no feature gates, and no licence keys.<\/p>\n\n<p>It handles the essentials cleanly and adds a full AI toolkit that uses either the Anthropic Claude API or the Google Gemini API to generate meta descriptions, ALT text, summaries, SEO scores, focus keywords, internal links, and related articles \u2014 all from WP Admin, with no extra subscriptions required.<\/p>\n\n<h4>Core SEO Features<\/h4>\n\n<ul>\n<li>Custom meta title and description per post and page<\/li>\n<li>Canonical URL output on every page<\/li>\n<li>OpenGraph tags (title, description, image, type, locale)<\/li>\n<li>Twitter\/X Card tags<\/li>\n<li>JSON-LD structured data: Person schema for author pages, Article\/BlogPosting schema for posts, WebSite schema for the homepage, Breadcrumb schema<\/li>\n<li>Configurable site name, locale, Twitter handle, and default OG image<\/li>\n<li>XML sitemap generation (sitemap.xml index + child sitemaps) with configurable post types and taxonomy support<\/li>\n<li>Plain-text sitemap at \/sitemap.txt (one URL per line) for AI crawlers and simple scrapers<\/li>\n<li>Custom robots.txt editor with AI bot blocking (GPTBot, CCBot, Claude-Web, anthropic-ai and others)<\/li>\n<li>llms.txt support for AI crawler guidance<\/li>\n<li>noindex controls for search results, 404 pages, attachment pages, author archives, and tag archives<\/li>\n<li>UTM parameter stripping in canonical URLs<\/li>\n<\/ul>\n\n<h4>AI Auto Pipeline<\/h4>\n\n<ul>\n<li>Automatically runs all AI operations in a separate background process the moment a post is published \u2014 no WP-Cron dependency<\/li>\n<li>Steps run per publish: meta description, SEO score, focus keyword, ALT text for all post images, AI-suggested internal links, AI summary box, and Related Articles<\/li>\n<li>Re-run on update toggle: re-triggers the full pipeline whenever a published post is saved<\/li>\n<li>Gutenberg-safe internal link injection using block-level parsing; classic editor fallback via str_replace<\/li>\n<li>Minimum 50-word content guard prevents meaningless output on stub or test posts<\/li>\n<li>HMAC-authenticated async request (120-second TTL) keeps the pipeline secure<\/li>\n<li>\"Re-run AI Automation\" button in the post metabox with live log output<\/li>\n<li>Auto Pipeline settings live in a dedicated card at the top of the AI Tools tab<\/li>\n<\/ul>\n\n<h4>AI Meta Writer<\/h4>\n\n<ul>\n<li>Choose your AI provider: Anthropic Claude or Google Gemini<\/li>\n<li>Model selector: Automatic (always resolves to 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<\/li>\n<li>Generate meta descriptions for individual posts or in bulk across your entire site<\/li>\n<li>Fix existing descriptions that are too short or too long<\/li>\n<li>Fix titles that are outside the optimal 50 to 60 character range<\/li>\n<li>Inline edit button on each post row \u2014 opens a textarea to manually enter or correct a description without leaving the panel<\/li>\n<li>Configurable character range (min\/max) injected into the prompt automatically<\/li>\n<li>Automatic retry if the AI returns a description outside your target range<\/li>\n<li>Rate limit handling with automatic backoff on HTTP 429 responses<\/li>\n<li>Fully editable system prompt with reset to default<\/li>\n<li>Sortable post table: sort by title, date, SEO score, description length, title length, or ALT status<\/li>\n<li>Live progress log with timestamps during bulk runs<\/li>\n<li>Stop button for interrupting bulk runs<\/li>\n<li>Scheduled batch generation via WP Cron with per-day scheduling<\/li>\n<li>Test Key button to verify your API key before running<\/li>\n<\/ul>\n\n<h4>AI SEO Scoring<\/h4>\n\n<ul>\n<li>AI rates each post from 0 to 100 with a one-sentence strengths or weaknesses note<\/li>\n<li>Calculate SEO Scores button runs a bulk scoring pass across all posts<\/li>\n<li>Generate Missing automatically scores any post that lacks a score after descriptions are written<\/li>\n<li>Per post score badges shown in the AI Tools post table and dashboard widget<\/li>\n<li>Scores stored in post meta (_cs_seo_score, _cs_seo_notes) and survive plugin deactivation<\/li>\n<\/ul>\n\n<h4>Title Optimiser<\/h4>\n\n<ul>\n<li>New \ud83c\udfaf Title Optimiser tab \u2014 AI scans all published posts and suggests SEO-optimised replacement titles<\/li>\n<li>Before\/after SEO score (0\u2013100) for every suggestion so you can see the improvement at a glance<\/li>\n<li>Identifies primary and secondary keywords the article is actually about<\/li>\n<li>One-click Apply per post: updates title and URL slug, automatically creates a 301 redirect from the old URL<\/li>\n<li>\"Apply All Suggested\" bulk action with confirmation \u2014 applies and redirects all in one shot<\/li>\n<li>Sort posts by date or by most-commented to prioritise which titles to fix first<\/li>\n<li>Suggestions stored in post meta \u2014 safe to pause, review, and apply selectively<\/li>\n<\/ul>\n\n<h4>AI Summary Box<\/h4>\n\n<ul>\n<li>AI-generated article summary box automatically prepended to post content<\/li>\n<li>Three fields generated per post: What it is, Why it matters, Key takeaway<\/li>\n<li>Summaries now written SEO-first: primary keyword front-loaded, secondary keywords woven in, optimised for search intent rather than conversational reading<\/li>\n<li>Bulk generation panel with progress tracking, stop button, and paginated post list<\/li>\n<li>Force regenerate option to overwrite all existing summaries<\/li>\n<li>Summary fields written to Article JSON-LD schema: description, abstract, and disambiguatingDescription<\/li>\n<li>Collapsible display with modern card styling including gradient header and drop shadow<\/li>\n<li>Toggle to show or hide the summary box globally without deleting generated content<\/li>\n<\/ul>\n\n<h4>ALT Text Generator<\/h4>\n\n<ul>\n<li>Audit all images across your posts for missing ALT text<\/li>\n<li>Generate ALT text using AI with article context for better relevance<\/li>\n<li>Configurable article excerpt length sent to the AI (100 to 2000 characters)<\/li>\n<li>Bulk generation with progress tracking<\/li>\n<li>Show All toggle to display images that already have ALT text<\/li>\n<\/ul>\n\n<h4>Related Articles<\/h4>\n\n<ul>\n<li>Automatically injects contextually related post links at the top and bottom of every post<\/li>\n<li>AI-scored candidate pool built across the full post library; top and bottom counts configurable (2 to 5 top, 3 to 10 bottom)<\/li>\n<li>Separate top and bottom toggles \u2014 enable or disable each block independently<\/li>\n<li>Generate Missing button runs the scoring pipeline for unprocessed posts<\/li>\n<li>Refresh Stale button re-runs previously completed posts when content has changed<\/li>\n<li>Sync Counts button trims or fills all posts to match updated count settings without full regeneration<\/li>\n<li>Post Status table shows per-post pipeline state (pending, complete, failed) with filter tabs<\/li>\n<li>All injection is block-safe and works with both Gutenberg and classic editor posts<\/li>\n<li>Related Articles links are also generated automatically via the Auto Pipeline on publish<\/li>\n<\/ul>\n\n<h4>Performance Features<\/h4>\n\n<ul>\n<li>Font display optimization with font-display: swap to eliminate Flash of Invisible Text (FOIT)<\/li>\n<li>Font metric overrides (size-adjust, ascent-override, descent-override) to reduce Cumulative Layout Shift (CLS)<\/li>\n<li>Defer font CSS loading using media=\"print\" swap technique<\/li>\n<li>Auto-download CDN fonts (Google Fonts) to local server for faster loading and GDPR compliance<\/li>\n<li>Font CSS file scanner with terminal-style console output<\/li>\n<li>Auto-Fix All with backup and undo capability<\/li>\n<li>Defer render-blocking JavaScript with configurable exclusions<\/li>\n<li>HTML, CSS, and JS minification (5 to 15 percent page size reduction)<\/li>\n<li>HTTPS mixed content scanner and one-click fixer across posts, pages, metadata, options, and comments<\/li>\n<\/ul>\n\n<h4>SEO Health Dashboard<\/h4>\n\n<ul>\n<li>Dashboard widget shows five health pillars: Posts (meta coverage), SEO (score coverage), Images (ALT coverage), Links (related articles coverage), Summaries (AI summary coverage)<\/li>\n<li>Colour-coded pills: green &gt;= 90%, amber &gt;= 60%, red &lt; 60%<\/li>\n<li>Refresh link rebuilds the health cache on demand; cache auto-rebuilds after any bulk AI run completes<\/li>\n<li>\"Posts need AI auto run\" and \"pipeline jobs queued\" counters keep you informed of pending work<\/li>\n<\/ul>\n\n<h4>Dashboard Integration<\/h4>\n\n<ul>\n<li>WordPress dashboard widget with SEO health overview and per-pillar coverage pills<\/li>\n<li>Post editor metabox with custom title, description, OG image, and inline AI generation<\/li>\n<li>Gutenberg sidebar panel (CloudScale Meta Boxes) with custom title, description, OG image, AI summary fields, and one-click generation without leaving the editor<\/li>\n<li>Per-post status badges showing description length, title length, SEO score, and ALT status<\/li>\n<li>Tab state persists across page reloads \u2014 the settings page returns to your last active tab<\/li>\n<\/ul>\n\n<h4>Category Fixer<\/h4>\n\n<ul>\n<li>Scans all published posts and suggests improved category assignments using AI<\/li>\n<li>Uses Claude to analyse post title, slug, tags, and AI summary box against your full category list<\/li>\n<li>Proposes up to four categories per post \u2014 only from categories that already exist in WordPress<\/li>\n<li>Never assigns Uncategorized<\/li>\n<li>Colour-coded review table: green for additions, red for removals, grey for kept categories<\/li>\n<li>Per post Apply and Skip buttons, plus bulk Apply All Changed<\/li>\n<li>Filter bar: All, Changed, Unchanged, Low Confidence, Missing<\/li>\n<li>Reload button re-analyses all posts with fresh AI calls<\/li>\n<li>Per row re-analyse button for individual posts<\/li>\n<li>AI analysis badge shows confidence score<\/li>\n<li>No categories are changed until you explicitly click Apply<\/li>\n<\/ul>\n\n<h4>Category Health and Drift Detection<\/h4>\n\n<ul>\n<li>Category Health tab shows post counts per category with a pass\/fail coverage indicator<\/li>\n<li>Category Drift Detection uses AI to identify categories that have drifted from their original focus or become catch-all buckets<\/li>\n<li>Drift analysis returns a verdict (drifting or catch-all) with a confidence score and AI reasoning for each flagged category<\/li>\n<li>Results sorted by verdict type then confidence so the most actionable items appear first<\/li>\n<li>Elapsed time counter and Stop button during long analysis runs<\/li>\n<\/ul>\n\n<h4>Readability Analyser<\/h4>\n\n<ul>\n<li>Pure-PHP readability scoring \u2014 no AI call required<\/li>\n<li>Scores 0\u2013100 with Easy \/ Moderate \/ Hard label based on sentence length, heading density, and passive-voice rate<\/li>\n<li>Colour-coded badge in the post metabox with sub-metrics (average words per sentence, words per heading, passive voice percentage)<\/li>\n<li>Sortable Readability column in the Meta Writer post list<\/li>\n<li>Scores automatically recalculate on post save and after every Auto Pipeline run<\/li>\n<\/ul>\n\n<h4>Broken Link Checker<\/h4>\n\n<ul>\n<li>Scans all published posts and pages for outbound links with HTTP errors (4xx, 5xx) or connection failures<\/li>\n<li>Server-side HEAD request per URL for accurate status \u2014 no browser-side fetch limitations<\/li>\n<li>Deduplicates URLs across posts so each external URL is checked only once<\/li>\n<li>Results table shows post title, anchor text, URL, and HTTP status with colour-coded labels<\/li>\n<li>SSRF-safe: link-local, loopback, and private IP ranges are blocked server-side<\/li>\n<\/ul>\n\n<h4>Image SEO Audit<\/h4>\n\n<ul>\n<li>Scans the entire Media Library and flags images with SEO issues<\/li>\n<li>Detects missing ALT text, camera-default filenames (IMG_001, DSC_0045, screenshot2, etc.), and oversized files (&gt; 500 KB)<\/li>\n<li>Results sorted by issue count with thumbnail previews and direct edit links<\/li>\n<li>Summary counters for each issue type<\/li>\n<\/ul>\n\n<h4>What This Plugin Does Not Do<\/h4>\n\n<ul>\n<li>No third-party SEO data, keyword research databases, or rank tracking<\/li>\n<li>No paid tiers, no upsells, no tracking<\/li>\n<\/ul>\n\n<h3>External Services<\/h3>\n\n<p>This plugin connects to external AI APIs to generate meta descriptions and ALT text. Connections are made when you use the AI Meta Writer or ALT Text Generator buttons in WP Admin, and optionally on a scheduled basis via WP Cron.<\/p>\n\n<h4>Anthropic Claude API<\/h4>\n\n<p><strong>Service:<\/strong> Anthropic PBC\n<strong>Website:<\/strong> https:\/\/anthropic.com\n<strong>Endpoint:<\/strong> https:\/\/api.anthropic.com\/v1\/messages\n<strong>Data sent:<\/strong> Post title and post content (up to 6,000 characters), plus your configured system prompt\n<strong>When data is sent:<\/strong> Only when you click Generate, Fix, or the Generate with Claude button in the post editor, when the scheduled batch runs for posts without a meta description, or automatically on post publish\/update when Auto Pipeline is enabled in AI Tools settings.\n<strong>API key:<\/strong> You must supply your own Anthropic API key. The key is stored in your WordPress database and is never transmitted anywhere except directly to api.anthropic.com.<\/p>\n\n<p>Anthropic Privacy Policy: https:\/\/www.anthropic.com\/privacy\nAnthropic Terms of Service: https:\/\/www.anthropic.com\/terms\nAnthropic API documentation: https:\/\/docs.anthropic.com<\/p>\n\n<h4>Google Gemini API<\/h4>\n\n<p><strong>Service:<\/strong> Google LLC\n<strong>Website:<\/strong> https:\/\/ai.google.dev\n<strong>Endpoint:<\/strong> https:\/\/generativelanguage.googleapis.com\/v1beta\/models\/\n<strong>Data sent:<\/strong> Post title and post content (up to 6,000 characters), plus your configured system prompt\n<strong>When data is sent:<\/strong> Only when you click Generate, Fix, or the scheduled batch runs, when Gemini is selected as your AI provider, or automatically on post publish\/update when Auto Pipeline is enabled in AI Tools settings.\n<strong>API key:<\/strong> You must supply your own Google AI API key. The key is stored in your WordPress database and is never transmitted anywhere except directly to Google.<\/p>\n\n<p>Google Privacy Policy: https:\/\/policies.google.com\/privacy\nGoogle Terms of Service: https:\/\/policies.google.com\/terms\nGemini API documentation: https:\/\/ai.google.dev\/docs<\/p>\n\n<h4>Google Fonts CDN<\/h4>\n\n<p><strong>Service:<\/strong> Google LLC\n<strong>Websites:<\/strong> https:\/\/fonts.googleapis.com, https:\/\/fonts.gstatic.com\n<strong>When contacted:<\/strong> Only when you use the Font Display Optimizer's \"Download Fonts\" feature in WP Admin (Performance tab). This feature downloads Google Fonts files to your server so they can be served locally.\n<strong>Data sent:<\/strong> The URL of the Google Font stylesheet registered on your site. No personal data or post content is transmitted.\n<strong>Purpose:<\/strong> To copy font files from Google's CDN to your own server, eliminating the external Google Fonts request from your frontend pages (improves GDPR compliance and Core Web Vitals).<\/p>\n\n<p>Google Privacy Policy: https:\/\/policies.google.com\/privacy\nGoogle Terms of Service: https:\/\/policies.google.com\/terms<\/p>\n\n<h4>Broken Link Checker (server-side URL probing)<\/h4>\n\n<p><strong>What it does:<\/strong> When you use the Broken Link Checker in WP Admin, the plugin extracts all outbound hyperlinks from your published posts and pages, then sends an HTTP HEAD request from your server to each unique URL to check its status.\n<strong>Data sent:<\/strong> Only the URL itself is fetched \u2014 no post content, no user data. Standard HTTP headers (User-Agent identifying your site) are sent with each request.\n<strong>When it fires:<\/strong> Only when you open the Broken Link Checker tab and start a scan. No automatic or scheduled scanning.\n<strong>Note:<\/strong> <code>sslverify<\/code> is disabled for these requests so that sites with expired or self-signed certificates can be checked. Requests to loopback, link-local, and private IP ranges are blocked.<\/p>\n\n<p>There is no separate terms of service for outbound HTTP HEAD requests \u2014 your server is simply fetching publicly reachable URLs listed in your own content.<\/p>\n\n<!--section=installation-->\n<ol>\n<li>Upload the plugin zip via Plugins &gt; Add New &gt; Upload Plugin, or extract to wp-content\/plugins\/cloudscale-seo-ai-optimizer\/<\/li>\n<li>Activate the plugin through the Plugins menu<\/li>\n<li>Go to Settings &gt; CloudScale SEO to configure your site name, OG image, and Person schema<\/li>\n<li>To use the AI Meta Writer, go to the Optimise SEO tab and enter your API key for either Anthropic Claude or Google Gemini<\/li>\n<li>Click Test Key to verify your key, then Load Posts to see your site description status<\/li>\n<li>Use Generate Missing to create descriptions for posts that have none, or Fix Descriptions to correct any that are outside your configured character range<\/li>\n<li>Visit the Performance tab to enable font optimization, JavaScript deferral, and HTML minification<\/li>\n<\/ol>\n\n<p><strong>Important:<\/strong> Deactivate any other SEO plugins (Yoast, RankMath, All in One SEO) before using this plugin to avoid duplicate meta tags in your page output.<\/p>\n\n<!--section=faq-->\n<dl>\n<dt id=\"do%20i%20need%20an%20api%20key%3F\"><h3>Do I need an API key?<\/h3><\/dt>\n<dd><p>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.<\/p><\/dd>\n<dt id=\"which%20ai%20provider%20should%20i%20choose%3F\"><h3>Which AI provider should I choose?<\/h3><\/dt>\n<dd><p>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.<\/p><\/dd>\n<dt id=\"how%20much%20does%20the%20api%20cost%3F\"><h3>How much does the API cost?<\/h3><\/dt>\n<dd><p>Generating descriptions for a typical 200 post blog costs approximately $1.20 to $1.50 using Claude Sonnet, or under $0.50 using Gemini Flash. Both providers charge per token. You control your own API key and billing directly with each provider.<\/p><\/dd>\n<dt id=\"will%20this%20conflict%20with%20yoast%20or%20rankmath%3F\"><h3>Will this conflict with Yoast or RankMath?<\/h3><\/dt>\n<dd><p>Yes. Running two SEO plugins simultaneously produces duplicate meta tags. Deactivate your existing SEO plugin before activating this one.<\/p><\/dd>\n<dt id=\"where%20are%20my%20meta%20descriptions%20stored%3F\"><h3>Where are my meta descriptions stored?<\/h3><\/dt>\n<dd><p>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.<\/p><\/dd>\n<dt id=\"can%20i%20use%20a%20custom%20system%20prompt%3F\"><h3>Can I use a custom system prompt?<\/h3><\/dt>\n<dd><p>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.<\/p><\/dd>\n<dt id=\"what%20happens%20if%20the%20generated%20description%20is%20the%20wrong%20length%3F\"><h3>What happens if the generated description is the wrong length?<\/h3><\/dt>\n<dd><p>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.<\/p><\/dd>\n<dt id=\"how%20does%20font%20display%20optimization%20work%3F\"><h3>How does font display optimization work?<\/h3><\/dt>\n<dd><p>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.<\/p><\/dd>\n<dt id=\"how%20does%20javascript%20deferral%20work%3F\"><h3>How does JavaScript deferral work?<\/h3><\/dt>\n<dd><p>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.<\/p><\/dd>\n<dt id=\"can%20i%20schedule%20automatic%20description%20generation%3F\"><h3>Can I schedule automatic description generation?<\/h3><\/dt>\n<dd><p>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.<\/p><\/dd>\n<dt id=\"what%20does%20auto%20pipeline%20do%3F\"><h3>What does Auto Pipeline do?<\/h3><\/dt>\n<dd><p>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.<\/p><\/dd>\n<dt id=\"what%20is%20the%20automatic%20model%20option%3F\"><h3>What is the Automatic model option?<\/h3><\/dt>\n<dd><p>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.<\/p><\/dd>\n<dt id=\"how%20does%20related%20articles%20work%3F\"><h3>How does Related Articles work?<\/h3><\/dt>\n<dd><p>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.<\/p><\/dd>\n\n<\/dl>\n\n<!--section=changelog-->\n<h4>4.20.64<\/h4>\n\n<ul>\n<li>Add: Broken Link Checker \u2014 Date Created column in results table; Post, Date Created, and Status headers are now sortable<\/li>\n<li>Fix: Broken Link Checker \u2014 sites returning 503 to server-side requests (Cloudflare JS-challenge) now treated as alive, eliminating false positives<\/li>\n<li>Add: Redirects \u2014 Created column moved next to Last hit and made sortable; Hits, Last hit, and Created all support click-to-sort<\/li>\n<li>Fix: Broken Link Checker \u2014 sites returning 401 to server-side requests (Reuters, WatchMojo) now treated as alive, eliminating false positives<\/li>\n<\/ul>\n\n<h4>4.20.27<\/h4>\n\n<ul>\n<li>Security: Broken Link Checker SSRF guard \u2014 <code>blc_is_ssrf_blocked()<\/code> now rejects URLs that resolve to loopback, link-local, or private IP ranges before making the <code>wp_remote_head()<\/code> call<\/li>\n<li>Fix: Title Optimiser stale detection \u2014 added 60-second grace period so applying a title never immediately flags the post as \"Edited since analysis\" due to <code>wp_update_post()<\/code> timing<\/li>\n<li>Fix: Title Optimiser applied-post hint text corrected from \"re-analyse to compare originals\" to \"Title was changed to suggested title\"<\/li>\n<li>Docs: Added Readability, Broken Link Checker, and Image SEO Audit feature sections to readme description<\/li>\n<li>Docs: Corrected \"What This Plugin Does Not Do\" \u2014 removed incorrect \"no readability scoring\" claim; added BLC to External Services section<\/li>\n<\/ul>\n\n<h4>4.20.2<\/h4>\n\n<ul>\n<li>Add: Title Optimiser \u2014 new \ud83c\udfaf 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<\/li>\n<li>Change: AI Summary Box now uses SEO-first prompts \u2014 primary keyword front-loaded in every field, secondary keywords woven in naturally, written for search intent; existing summaries unchanged until regenerated<\/li>\n<\/ul>\n\n<h4>4.19.142<\/h4>\n\n<ul>\n<li>Add: Readability scoring \u2014 pure-PHP analysis of sentence length, heading density, and passive-voice rate; no AI call required; scores 0\u2013100 with Easy \/ Moderate \/ Hard labels<\/li>\n<li>Add: Readability badge in post metabox \u2014 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<\/li>\n<li>Add: Sortable Readability column in Meta Writer post list \u2014 badge updates live during pipeline run<\/li>\n<li>Add: Auto-pipeline and save_post hook both trigger readability scoring to keep scores fresh<\/li>\n<li>Add: Migrate Categories panel in the Categories tab \u2014 lists all categories sorted by post count (fewest first) and lets you migrate posts category-by-category<\/li>\n<li>Add: Single-category posts require a swap target; multi-category posts can be removed or swapped per row<\/li>\n<li>Add: Apply per-row or Apply All to batch-migrate every pending post in one click<\/li>\n<li>Add: Delete Category button appears automatically once a category is empty \u2014 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<\/li>\n<li>Fix: Settings save safety net \u2014 new option keys no longer silently drop on save<\/li>\n<\/ul>\n\n<h4>4.19.126<\/h4>\n\n<ul>\n<li>Add: Target audience and Writing tone fields in Site Identity \u2014 injected into every AI request; largest quality improvement available without editing the system prompt<\/li>\n<li>Add: Prominent callout banner above new fields nudging users to fill them in before generating<\/li>\n<li>Improve: Default AI system prompt rewritten to actively use site context for niche and voice matching<\/li>\n<li>Improve: Site context header in all AI call sites updated from passive label to active instruction<\/li>\n<li>Improve: Help documentation \u2014 Auto Pipeline and Redirects now have screenshots; new fields documented; Explain buttons tip added<\/li>\n<li>Fix: Right-side padding too tight \u2014 added padding-right to .ab-pane so card shadow is not clipped<\/li>\n<\/ul>\n\n<h4>4.19.99<\/h4>\n\n<ul>\n<li>Add: Initial implementation of Target audience and Writing tone site context fields<\/li>\n<\/ul>\n\n<h4>4.19.98<\/h4>\n\n<ul>\n<li>Fix: PCP critical \u2014 raw script tag in redirects admin moved to wp_add_inline_script to comply with WordPress.org standards<\/li>\n<li>Fix: delete-redirect and clear-all-redirects fetch calls now have catch handlers for network errors<\/li>\n<li>Fix: PCP NonceVerification.Missing \u2014 all AJAX handlers now call check_ajax_referer() directly in scope (was delegated via helper wrapper)<\/li>\n<li>Fix: esc_attr() added to colour ternary expression in batch history table<\/li>\n<li>Fix: removed stale \"No redirect management\" line from readme<\/li>\n<\/ul>\n\n<h4>4.19.90<\/h4>\n\n<ul>\n<li>Fix: Settings save broken for all checkbox fields \u2014 added hidden fallback inputs so unchecked checkboxes correctly save 0<\/li>\n<li>Fix: Redirects zone-header white-on-white \u2014 added teal background colour to redirects card header<\/li>\n<li>Fix: Manual redirect form and stored redirects table were rendering outside the card container<\/li>\n<li>Fix: Save button left padding corrected across all admin cards<\/li>\n<li>Add: Playwright settings-save tests covering all panels<\/li>\n<\/ul>\n\n<h4>4.19.89<\/h4>\n\n<ul>\n<li>Fix: Redirects zone-header white-on-white \u2014 added background colour to redirects card header<\/li>\n<li>Fix: Add Manual Redirect and Stored Redirects sections moved inside the card container<\/li>\n<li>Fix: Save button left padding corrected across all admin cards<\/li>\n<\/ul>\n\n<h4>4.19.88<\/h4>\n\n<ul>\n<li>Change: Sitemap &amp; Robots tab renamed to Sitemap, Robots &amp; Redirects<\/li>\n<li>Change: Redirects section moved to Sitemap, Robots &amp; Redirects tab<\/li>\n<\/ul>\n\n<h4>4.19.87<\/h4>\n\n<ul>\n<li>Change: Redirects section moved to bottom of Optimise SEO tab with zone-card styling and Explain button<\/li>\n<\/ul>\n\n<h4>4.19.86<\/h4>\n\n<ul>\n<li>Fix: Manual redirect row was inserting into wrong table element \u2014 fixed tbody selector<\/li>\n<\/ul>\n\n<h4>4.19.85<\/h4>\n\n<ul>\n<li>Add: Automatic Redirects \u2014 301 redirect automatically captured and served when a published post or page slug is renamed<\/li>\n<li>Add: Manual redirect form \u2014 add custom path\u2192URL redirects for any resource including image paths and arbitrary old paths<\/li>\n<li>Add: Hit counter and last-hit timestamp on every redirect entry, displayed inline next to the old path<\/li>\n<li>Add: Clickable old-path links in the redirect table for one-click testing<\/li>\n<li>Fix: Save Changes on the Redirects tab was silently ignored \u2014 enable_redirects added to sanitize_opts known-fields guard<\/li>\n<li>Fix: 301 redirects not firing \u2014 moved hook to template_redirect priority 0 to run before cs_pcr_maybe_custom_404 which exits at priority 1<\/li>\n<li>Fix: enable_redirects default changed to enabled (1) for fresh installs<\/li>\n<\/ul>\n\n<h4>4.19.72<\/h4>\n\n<ul>\n<li>Update: version bump<\/li>\n<\/ul>\n\n<h4>4.19.50<\/h4>\n\n<ul>\n<li>Add: \"Automatic\" model option \u2014 new default that always resolves to the current recommended model per provider (claude-sonnet-4-6 \/ gemini-2.0-flash); existing users with a pinned model are unaffected<\/li>\n<\/ul>\n\n<h4>4.19.11<\/h4>\n\n<ul>\n<li>Add: sitemap.txt endpoint \u2014 plain-text sitemap (one URL per line) served at \/sitemap.txt alongside the existing XML sitemap<\/li>\n<\/ul>\n\n<h4>4.19.4<\/h4>\n\n<ul>\n<li>Fix: debug_log() method added to main class \u2014 font optimizer and OG letterbox AJAX handlers threw fatal errors in production; now delegates to Utils::log() for a single logging code path<\/li>\n<li>Fix: orphaned duplicate DocBlocks removed from trait-ai-meta-writer.php<\/li>\n<li>Fix: missing DocBlocks added across main class, trait-ai-meta-writer.php, and trait-settings-assets.php<\/li>\n<li>Fix: @package tag added to trait-settings-assets.php file DocBlock<\/li>\n<\/ul>\n\n<h4>4.19.3<\/h4>\n\n<ul>\n<li>PCP: moved phpcs:ignore for post__not_in to same line as violation in trait-auto-pipeline.php, trait-ai-meta-writer.php, and trait-sitemap.php \u2014 inline suppression is required for PHPCS to recognise it<\/li>\n<\/ul>\n\n<h4>4.19.2<\/h4>\n\n<ul>\n<li>PCP fix: echoed  tag in auto-run metabox replaced with wp_add_inline_script()<\/li>\n<li>Fix: button label '\\u21ba' in single-quoted PHP string replaced with literal UTF-8 \u21ba<\/li>\n<li>Fix: inline conditional colour echo in metabox log wrapped with esc_attr()<\/li>\n<li>Fix: orphaned DocBlock on on_post_delete restored; duplicate consecutive DocBlock removed<\/li>\n<li>Fix: render_rc_block() DocBlock missing @since and @return \u2014 added<\/li>\n<li>CHANGELOG.md synced \u2014 all versions from 4.14.6 through 4.19.1 back-filled<\/li>\n<\/ul>\n\n<h4>4.19.1<\/h4>\n\n<ul>\n<li>Added Explain buttons to Auto Pipeline, Mixed Content Fix, and Render &amp; Minification cards<\/li>\n<li>Updated Auto Pipeline description to reflect non-blocking HTTP approach (not cron)<\/li>\n<\/ul>\n\n<h4>4.19.0<\/h4>\n\n<ul>\n<li>Auto pipeline: all AI steps (meta description, focus keyword, internal links, AI summary) now require a minimum of 50 words of post content before running \u2014 prevents meaningless output on stub\/test posts<\/li>\n<\/ul>\n\n<h4>4.18.9<\/h4>\n\n<ul>\n<li>AI auto pipeline now uses non-blocking wp_remote_post() instead of WP-Cron \u2014 fires immediately on publish with no cron dependency<\/li>\n<li>HMAC token (120s TTL transient) authenticates the async pipeline request<\/li>\n<li>Removed cs_seo_auto_run_pipeline cron hook; cleanup pipeline retains cron<\/li>\n<\/ul>\n\n<h4>4.18.8<\/h4>\n\n<ul>\n<li>Related Articles now run synchronously on publish via transition_post_status \u2014 no cron dependency<\/li>\n<li>AI pipeline now calls spawn_cron() on shutdown after scheduling to force immediate cron execution<\/li>\n<\/ul>\n\n<h4>4.18.7<\/h4>\n\n<ul>\n<li>Reload button moved left of Show\/Hide Details button and uses visibility:hidden so the layout never shifts when it appears<\/li>\n<\/ul>\n\n<h4>4.18.6<\/h4>\n\n<ul>\n<li>Auto Pipeline card moved to AI Tools tab (top of panel) with its own Save button<\/li>\n<li>Added \"Re-run on update\" toggle \u2014 re-runs full pipeline 5 seconds after any published post is saved<\/li>\n<li>Auto Pipeline settings removed from Scheduled Batch card<\/li>\n<\/ul>\n\n<h4>4.18.5<\/h4>\n\n<ul>\n<li>Removed duplicate Reload buttons from inside AI Meta Writer, ALT Text, and Summary Box toolbars \u2014 Reload only appears in the card header<\/li>\n<\/ul>\n\n<h4>4.18.4<\/h4>\n\n<ul>\n<li>Fix: altLoad() and sumLoad() were crashing on removed button elements, preventing auto-load on card expand<\/li>\n<\/ul>\n\n<h4>4.18.3<\/h4>\n\n<ul>\n<li>Removed all \"Hide Posts\" buttons from AI Meta Writer, ALT Text, Summary Box, Category Fixer, and Category Health cards<\/li>\n<\/ul>\n\n<h4>4.18.2<\/h4>\n\n<ul>\n<li>Removed \"Load Posts\" and \"Scan Posts\" CTA buttons from AI Meta Writer, ALT Text, and Summary Box cards<\/li>\n<li>Cards now auto-load their posts on first \"Show Details\" expand via abToggleCard<\/li>\n<\/ul>\n\n<h4>4.18.1<\/h4>\n\n<ul>\n<li>Auto Pipeline: added \"Auto Run on publish\" toggle in Batch\/Schedule settings \u2014 pipeline is disabled by default until explicitly enabled<\/li>\n<li>Auto Pipeline: on_post_publish and on_post_delete now check the toggle before scheduling cron events<\/li>\n<\/ul>\n\n<h4>4.18.0<\/h4>\n\n<ul>\n<li>Auto Pipeline: new trait-auto-pipeline.php \u2014 all AI operations run automatically via WP-Cron when a post is published<\/li>\n<li>Auto Pipeline: steps \u2014 meta description + SEO score, focus keyword, ALT text for attached images, internal links (AI-suggested, block-safe injection), AI summary box, Related Articles generation<\/li>\n<li>Auto Pipeline: Gutenberg-safe internal link injection using parse_blocks \/ serialize_blocks on core\/paragraph and core\/heading blocks; classic editor fallback via str_replace<\/li>\n<li>Auto Pipeline: delete cleanup pipeline removes all <em>cs<\/em> post meta and run log transients for permanently deleted posts<\/li>\n<li>Auto Pipeline: \"Re-run AI Automation\" button in post edit screen metabox with live log display<\/li>\n<li>Auto Pipeline: CSEO_ASYNC_ENABLED constant for synchronous debugging mode<\/li>\n<li>Dashboard widget: \"posts need AI auto run\" and \"pipeline jobs queued\" metrics added<\/li>\n<li>New meta keys: _cs_seo_auto_run_complete, _cs_seo_focus_keyword<\/li>\n<\/ul>\n\n<h4>4.17.6<\/h4>\n\n<ul>\n<li>Related Articles Post Status: \"All Posts\" now uses a direct DB query instead of WP_Query, fixing the environment where WP_Query with no meta restriction returned 0 results (new posts were invisible)<\/li>\n<li>Related Articles Post Status: \"All Posts\" is now the default filter so newly published articles appear immediately on table load<\/li>\n<\/ul>\n\n<h4>4.17.5<\/h4>\n\n<ul>\n<li>File-level DocBlocks (@package, @since) added to all 20 remaining trait files<\/li>\n<li>Settings page: all form  field labels and RC table labels wrapped in esc_html_e()<\/li>\n<li>ajax_rc_sync_counts @since history corrected to reflect 4.16.5, 4.17.1, 4.17.2 changes<\/li>\n<\/ul>\n\n<h4>4.17.4<\/h4>\n\n<ul>\n<li>PCP: removed load_plugin_textdomain() call \u2014 discouraged since WP 4.6, auto-loaded by WordPress.org<\/li>\n<li>PCP: added missing translators comment to printf in metabox<\/li>\n<li>PCP: removed set_time_limit() call in ajax_rc_sync_counts<\/li>\n<li>PCP: prefixed global variables in uninstall.php ($cs_seo_options, $cs_seo_meta_keys, etc.)<\/li>\n<li>PCP: added phpcs:ignore to Utils class declaration with explanation<\/li>\n<\/ul>\n\n<h4>4.17.3<\/h4>\n\n<ul>\n<li>Settings page now returns to the active tab after saving \u2014 active tab is saved to localStorage and restored on page reload<\/li>\n<\/ul>\n\n<h4>4.17.2<\/h4>\n\n<ul>\n<li>Fix: Generate &amp; Sync now correctly decreases link counts for posts where _cs_rc_scores was deleted by earlier reset attempts \u2014 added a third code path that directly trims top\/bottom arrays without requiring scores or re-running the pipeline<\/li>\n<li>Fix: button label showed \"&amp;\" literal after completion \u2014 changed to textContent with plain \"&amp;\"<\/li>\n<\/ul>\n\n<h4>4.17.1<\/h4>\n\n<ul>\n<li>Merged \"Generate Missing\" and \"Sync Counts\" into one \"Generate &amp; Sync\" button \u2014 single server-side pass that runs the full pipeline for posts with no scores and re-applies count settings for posts that already have scores<\/li>\n<\/ul>\n\n<h4>4.17.0<\/h4>\n\n<ul>\n<li>Related Articles Post Status table: post title links now open the live post URL (so related links are visible) instead of the editor<\/li>\n<\/ul>\n\n<h4>4.16.9<\/h4>\n\n<ul>\n<li>Fix: post title links in Related Articles Post Status table now open the post editor instead of the category editor (was using a relative URL that inherited the settings page context)<\/li>\n<\/ul>\n\n<h4>4.16.8<\/h4>\n\n<ul>\n<li>Fix: clicking Generate Missing when all posts are complete no longer leaves the table showing \"No posts found.\" \u2014 the table restores to its previous filter\/page after the alert<\/li>\n<\/ul>\n\n<h4>4.16.7<\/h4>\n\n<ul>\n<li>Sync Counts now fills upward as well as trimming \u2014 re-applies the stored candidate scores to fill additional slots when the count setting is increased, so no full regeneration is needed<\/li>\n<\/ul>\n\n<h4>4.16.6<\/h4>\n\n<ul>\n<li>Fix: Related Articles settings (rc_top_count, rc_bottom_count, rc_enable) were silently discarded on save \u2014 the options sanitizer's known-fields guard did not include any RC keys so every RC form submission was rejected as spurious<\/li>\n<\/ul>\n\n<h4>4.16.5<\/h4>\n\n<ul>\n<li>New: \"Sync Counts\" button in Related Articles Post Status \u2014 single server-side pass that trims all complete posts' stored links to match current Top\/Bottom count settings using a direct DB query, bypassing all WP_Query environment issues<\/li>\n<\/ul>\n\n<h4>4.16.4<\/h4>\n\n<ul>\n<li>Fix: Generate Missing now force-reloads with filter=pending before collecting IDs \u2014 previously it used the complete-post DOM rows and then filtered them all out, yielding 0 posts<\/li>\n<\/ul>\n\n<h4>4.16.3<\/h4>\n\n<ul>\n<li>Fix: rcRunOne final state fetch now uses rcCurrentFilter instead of hardcoded filter=all (which returns 0 in some environments), so row counts update live during a batch run<\/li>\n<li>Fix: Reset All reloads with current filter rather than filter=all<\/li>\n<li>Batch bar now shows post count at start so you can confirm the batch is running<\/li>\n<\/ul>\n\n<h4>4.16.2<\/h4>\n\n<ul>\n<li>Fix: Related Articles table autoload now uses filter=complete instead of filter=all (all-posts query returns 0 in some environments)<\/li>\n<li>Fix: rcBatch auto-reloads the table with the target filter before collecting IDs if the DOM is empty, guaranteeing posts are found<\/li>\n<\/ul>\n\n<h4>4.16.1<\/h4>\n\n<ul>\n<li>Fix: rcBatch now reads post IDs from the visible DOM table rows instead of a pre-fetch API call; eliminates \"No posts to process\" caused by cs_rc_get_posts returning 0 for certain filter\/query combinations<\/li>\n<\/ul>\n\n<h4>4.16.0<\/h4>\n\n<ul>\n<li>Fix: Refresh Stale now queries filter='complete' instead of filter='all' \u2014 semantically correct (re-runs previously completed posts) and resolves \"No posts to process\" caused by the all-posts query returning nothing in some environments<\/li>\n<\/ul>\n\n<h4>4.15.9<\/h4>\n\n<ul>\n<li>Refactor: rcBatch rewritten to use a page-1 probe for total count then process page-by-page, eliminating the pre-fetch bulk-collect approach that returned 0 posts in some environments<\/li>\n<\/ul>\n\n<h4>4.15.8<\/h4>\n\n<ul>\n<li>Fix: Refresh Stale and Retry Failed now actually regenerate posts \u2014 previously the step handler returned immediately for complete posts without running any steps; each post is now reset to pending before re-running<\/li>\n<\/ul>\n\n<h4>4.15.7<\/h4>\n\n<ul>\n<li>Fix: Related Articles batch (Refresh Stale, Generate Missing, Retry Failed) now fetches all pages before building the queue \u2014 previously only the first 50 posts were ever processed<\/li>\n<\/ul>\n\n<h4>4.15.6<\/h4>\n\n<ul>\n<li>AI Tools post table: \u270f Edit button on each row opens an inline textarea to manually enter or edit the meta description, with Save and Cancel<\/li>\n<\/ul>\n\n<h4>4.15.5<\/h4>\n\n<ul>\n<li>PCP medium fixes: load_textdomain moved from plugins_loaded to init<\/li>\n<li>PHP version notice now uses i18n functions with translators comment<\/li>\n<li>DocBlocks added to all methods in trait-options.php<\/li>\n<li>File-level DocBlocks added to trait-schema.php and trait-ai-engine.php<\/li>\n<li>dispatch_ai() and ajax_check() DocBlocks completed with @since, @param, @return<\/li>\n<li>Created includes\/class-cloudscale-seo-ai-optimizer-utils.php Utils class<\/li>\n<li>Settings page: tab labels, zone headers, and all submit_button labels wrapped in i18n functions<\/li>\n<\/ul>\n\n<h4>4.15.4<\/h4>\n\n<ul>\n<li>PCP compliance: JSON-LD structured data now output via wp_print_inline_script_tag() instead of echoed  strings \u2014 eliminates the only remaining critical PCP violation<\/li>\n<\/ul>\n\n<h4>4.15.3<\/h4>\n\n<ul>\n<li>Fix: PHP Warning \"Undefined array key message\" in batch scheduler log display \u2014 timeout and sum_ok entries have no message key<\/li>\n<\/ul>\n\n<h4>4.15.2<\/h4>\n\n<ul>\n<li>Scoring status bar now shows \"(Post N of Total)\" counter for both Calculate SEO Scores and Generate Missing phase 2<\/li>\n<\/ul>\n\n<h4>4.15.1<\/h4>\n\n<ul>\n<li>Fix: Generate Missing phase 2 scoring now does its own fresh post fetch instead of relying on phase 1 data; always logs phase 2 status<\/li>\n<\/ul>\n\n<h4>4.15.0<\/h4>\n\n<ul>\n<li>Generate Missing now runs a second phase that scores any posts still missing an SEO score after descriptions are done<\/li>\n<li>Renamed \"Score All\" button to \"Calculate SEO Scores\"<\/li>\n<\/ul>\n\n<h4>4.14.9<\/h4>\n\n<ul>\n<li>AI Tools post table: Description, Title, and ALT columns are now sortable; all six data columns now have clickable sort headers<\/li>\n<\/ul>\n\n<h4>4.14.8<\/h4>\n\n<ul>\n<li>Fix: homepage SEO score no longer disappears on reload \u2014 static front page row now reads seo_score\/seo_notes from post meta<\/li>\n<\/ul>\n\n<h4>4.14.7<\/h4>\n\n<ul>\n<li>AI Tools post table: added Date column and sortable headers for Post (title), Date, and SEO Score<\/li>\n<\/ul>\n\n<h4>4.14.6<\/h4>\n\n<ul>\n<li>Swapped Categories and Scheduled Batch tab order in settings page<\/li>\n<\/ul>\n\n<h4>4.13.2<\/h4>\n\n<ul>\n<li>Category Drift: cdLoad() now shows elapsed time counter and Stop button during analysis<\/li>\n<li>Category Drift: cdAnalyseRemaining() now shows post count in loading label and Stop button<\/li>\n<li>Both use AbortController so Stop cancels the in-flight fetch immediately<\/li>\n<\/ul>\n\n<h4>4.13.1<\/h4>\n\n<ul>\n<li>Fixed PHP operator-precedence bug in defer_font_css() noscript href \u2014 preg_match result was being concatenated before the ternary, making href always empty<\/li>\n<li>Added i18n (esc_html__\/esc_html_e) to user-visible strings in admin notice, metabox labels\/buttons, and frontend summary box<\/li>\n<li>Extracted admin_page_css(), llms_preview_js(), sitemap_preview_js() to trait-settings-assets.php (reduces trait-settings-page.php by ~300 lines)<\/li>\n<\/ul>\n\n<h4>4.13.0<\/h4>\n\n<ul>\n<li>Created CHANGELOG.md in Keep-a-Changelog format<\/li>\n<li>Dashboard widget title now uses wp_kses_post() with esc_html() for the version span<\/li>\n<li>Added @since, @param, @return DocBlocks to all public methods across all 23 trait files<\/li>\n<\/ul>\n\n<h4>4.0.0 \u2013 4.12.9<\/h4>\n\n<ul>\n<li>For the complete version history of all earlier releases see CHANGELOG.md in the plugin directory.<\/li>\n<\/ul>","raw_excerpt":"AI-powered SEO &amp; AEO: meta descriptions, auto linking, category management, ALT text. Bring your own Claude or Gemini API key. Free, open source.","jetpack_sharing_enabled":true,"_links":{"self":[{"href":"https:\/\/hat.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin\/285903","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/hat.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin"}],"about":[{"href":"https:\/\/hat.wordpress.org\/plugins\/wp-json\/wp\/v2\/types\/plugin"}],"replies":[{"embeddable":true,"href":"https:\/\/hat.wordpress.org\/plugins\/wp-json\/wp\/v2\/comments?post=285903"}],"author":[{"embeddable":true,"href":"https:\/\/hat.wordpress.org\/plugins\/wp-json\/wporg\/v1\/users\/andrewjbaker"}],"wp:attachment":[{"href":"https:\/\/hat.wordpress.org\/plugins\/wp-json\/wp\/v2\/media?parent=285903"}],"wp:term":[{"taxonomy":"plugin_section","embeddable":true,"href":"https:\/\/hat.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_section?post=285903"},{"taxonomy":"plugin_tags","embeddable":true,"href":"https:\/\/hat.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_tags?post=285903"},{"taxonomy":"plugin_category","embeddable":true,"href":"https:\/\/hat.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_category?post=285903"},{"taxonomy":"plugin_contributors","embeddable":true,"href":"https:\/\/hat.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_contributors?post=285903"},{"taxonomy":"plugin_business_model","embeddable":true,"href":"https:\/\/hat.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_business_model?post=285903"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}