Fifteenth Labs AI Search Ready

Description

This plugin doesn’t get your content found by AI search — your content does. What AI systems (ChatGPT, Perplexity, Google AI Overviews, etc.) actually find, understand, and cite is the substance and structure of what you’ve written. This plugin can’t change that; what it does is check your content against a framework of structure, clarity, and technical signals, and show you specifically what to fix — a checklist and a set of concrete edits, not a shortcut around doing the work.

Fifteenth Labs AI Search Ready analyzes your WordPress content against an AI-readiness framework covering structured data, content quality, semantic structure, and technical crawlability. Your score reflects how well your content matches these signals, based on this plugin’s own scoring methodology — it is not a live measurement of how any specific AI system will treat your content, and is not a guarantee of visibility, ranking, inclusion, or citation.

What It Checks

Structured Data (25% weight)
* JSON-LD Schema.org markup (Article, FAQ, HowTo, etc.)
* Relevant structured-data types
* Author entity and E-E-A-T signals
* Breadcrumb markup
* Date metadata

Content Quality (30% weight)
* Content length and depth
* Flesch readability score
* Opening paragraph clarity
* Direct answer patterns (“X is…”, “Steps to…”)
* Topic focus and keyword consistency
* Specific evidence (stats, data, quotes)

Semantic Structure (20% weight)
* Heading hierarchy (H2-H6)
* Paragraph structure
* List usage (ul/ol)
* Semantic HTML5 elements
* Internal linking
* Image alt text

Meta & Discoverability (15% weight)
* Title length and descriptiveness
* Meta description
* Taxonomy usage (categories + tags)
* URL readability

Technical AI-Readiness (10% weight)
* Crawl access (noindex check)
* Server-rendered content
* FAQ/Q&A patterns
* Content navigation (anchor links)
* Content freshness
* OpenGraph tags

Features

  • Per-post analysis via meta box in the post editor
  • Bulk analysis of all published content
  • Dashboard with site-wide score overview
  • AI Score column in post/page list tables
  • Auto-analyze on publish/update (configurable)
  • REST API for programmatic access
  • Prioritized recommendations sorted by impact

Important Note on Scoring

Fifteenth Labs AI Search Ready does not query ChatGPT, Perplexity, Google AI Overviews, or any other AI system to test how they treat your content. Scores are based on Fifteenth Labs AI Search Ready’s own analysis framework — structured data, content quality, semantic structure, metadata, and technical crawlability — and are intended as guidance to help you improve your content’s AI-readiness. They are not a guarantee of visibility, ranking, inclusion, or citation by any AI-powered search service.

Screenshots

Installation

  1. Upload the fifteenth-labs-ai-search-ready folder to /wp-content/plugins/
  2. Activate the plugin through the ‘Plugins’ menu
  3. Go to Fifteenth Labs AI Search Ready Dashboard to get started
  4. Edit any post/page to see the AI Score meta box

FAQ

Does this replace my SEO plugin?

No. This plugin focuses specifically on AI-readiness — how well your content aligns with Fifteenth Labs AI Search Ready’s framework for structure, clarity, metadata, and machine readability. It complements traditional SEO plugins.

Will this plugin get my content found by AI search?

No — and be wary of anything that claims it can. No plugin can guarantee that ChatGPT, Perplexity, Google AI Overviews, or any other AI system will find, use, or cite a specific page; none of them publish how they select sources, and that can change at any time. What actually determines whether AI systems find and use your content is the content itself — its substance, clarity, and structure. This plugin doesn’t change your content for you. It checks it against a framework of structure and clarity signals, and shows you specifically what to fix. The work of writing genuinely useful, well-organized content is still yours to do; this just tells you where it currently falls short.

What score should I aim for?

80+ means your content passes most of Fifteenth Labs AI Search Ready’s checks. 60–79 means there’s room to improve. Below 60 flags significant gaps against the checklist. These thresholds are based on Fifteenth Labs AI Search Ready’s own analysis framework and are intended as guidance — not a guarantee of visibility, ranking, inclusion, or citation by any AI-powered search service.

Does it work with Yoast/Rank Math?

Yes. It detects and accounts for structured data provided by major SEO plugins.

Does this plugin send my content anywhere?

No. When analyzing a page, it may fetch that same page’s own public URL — on your own site, nothing external — to read what’s actually rendered there (useful for page builders and other content stored outside the standard editor field). No content, scores, or site data are ever sent to Fifteenth Labs, any third-party service, or any AI system. All analysis runs locally on your own server.

Reviews

There are no reviews for this plugin.

Contributors & Developers

“Fifteenth Labs AI Search Ready” is open source software. The following people have contributed to this plugin.

Contributors

Changelog

1.2.1

  • Renamed the plugin to Fifteenth Labs AI Search Ready and updated Plugin URI / Author URI to fifteenthlabs.com, for directory submission consistency.
  • Moved the Bulk Analyze and Email Report admin pages’ inline scripts into properly enqueued files, with JSON_HEX_TAG protection on embedded report HTML.
  • Scoped the admin stylesheet to only load on this plugin’s own pages, the post editor, and post list screens, instead of every admin screen.
  • Fixed the Installation section to reference the current folder name.

1.2.0

  • Prepared for public release: standardized plugin header, updated author/contributor info
  • Security: added object-level capability checks (edit_post per post ID) on single-post REST routes and the Action Plan admin page, preventing users from accessing or triggering analysis on posts they don’t have permission to edit
  • Fixed: the page classifier read raw post_content directly to detect empty/placeholder pages, which misclassified any post type that stores its content outside the standard editor (e.g. custom post types with content in meta fields, some page builders) as an empty page and skipped analysis entirely — even when published with substantial real content. The classifier now uses the same resolved content (page builder aware, with a live-page-fetch fallback) that the rest of the analysis already relies on.
  • Fixed: clicking “Re-Analyze” on the standalone Action Plan page correctly re-ran the analysis and updated the stored report, but the visible score circle, action list, and counts on that page never refreshed — they’re server-rendered PHP with no matching live-update path, unlike the meta box and dashboard list table, which do update in place. A successful re-analyze on this page now reloads it so what’s shown matches what was just calculated.
  • Fixed: the JSON-LD Schema and Rich Schema Type checks never looked at the actual page — they guessed “found” from whether an SEO plugin’s PHP constant was defined (regardless of whether that plugin outputs anything for this specific post) and guessed the schema @type from the post type name (Article for ‘post’, WebPage for ‘page’, nothing for anything else, including custom post types). Both checks now fetch the live page and read its real JSON-LD script blocks — including schema written to dynamically by a theme, a custom post type’s own schema code, or an SEO plugin — falling back to the old heuristic only if the fetch itself fails.
  • Added: a “Diagnose Content Resolution” button on the Action Plan page, next to Re-Analyze. Runs the existing (previously REST-only) content-resolution diagnostic and shows the result inline — which strategy found the post’s content, how much text it found, and what meta keys are involved — useful for confirming whether a low score is a real content gap or a resolution issue, without needing to construct and authenticate a REST request by hand.
  • Fixed: content resolution tried a generic “scan all post meta for the single longest field” guess before trying to fetch and read the actual live page. That guess is right for BeTheme’s one-big-serialized-blob storage pattern (what it was built for), but wrong for any post type that splits real content across several meaningful fields — like a case study with separate brief/approach/outcome fields — where it would silently keep only the single longest field and discard the rest, understating word count and missing all real headings (which only exist in the rendered template, not in any one raw field). The live-page fetch now runs first, since it reflects everything actually on the page regardless of how many fields feed it; the meta scan remains as a fallback for when the live fetch itself fails.
  • Fixed: the FAQ-section suggestion (and its JSON-LD example) rendered with a blank topic — “What is ?” instead of “What is X?” — because generate_technical_actions() used $title without ever receiving it as a parameter. Silently undefined in PHP rather than an error, so it just came through empty. $title is now correctly passed through.
  • Fixed: the Topic Focus check extracted “keywords” from the title using a plain word-length filter (anything over 3 characters) with no stopword list, so pure function words like “that” got treated as equally meaningful topic keywords as real subject nouns — most visible on titles written as full sentences rather than short topic phrases. Both the check and its action-plan suggestion text (previously two separate, silently-drifting copies of the same logic) now share one keyword-extraction helper with an actual stopword list.
  • Fixed: the internal-links suggestion matched “related” posts by category, but WordPress automatically assigns any uncategorized post to a default “Uncategorized” category rather than leaving it with none — so any two posts that simply hadn’t been categorized yet were treated as related to each other regardless of actual subject matter, surfacing suggestions with no real topical connection. The default category is no longer treated as a relatedness signal; a post falls back to the generic linking advice until it has a real category assigned.
  • Changed: renamed and reworded the seven most jargon-heavy check names and their explanations (Canonical URL, OpenGraph, JSON-LD Schema, Rich Schema Type, Author Entity/E-E-A-T, Breadcrumb Markup, Readability/Flesch Score) into plain language, since these assumed the reader already knew SEO/technical-AI terminology. The original technical term is kept in parentheses in each explanation for anyone who wants to look it up, so nothing is lost for advanced users — the goal was making the plugin usable for someone with no SEO background, not hiding the substance.
  • Added: a new “Quick Fixes” page. On a site with many pages, opening each page’s Action Plan individually to fix the same simple issue over and over doesn’t scale. This page instead lists each of the four safe, mechanical issues (missing/wrong-length meta description, missing image alt text, no categories or tags, an unreadable URL slug) across every affected page or image site-wide, with an inline field and Save button per row — fixed directly, no need to open the post editor. Deliberately limited to short, low-risk fields; anything touching actual body content (headings, sentence rewrites, new sections) stays in the per-page Action Plan, next to the content it changes.
  • Added: uninstall.php, so deleting the plugin (not just deactivating it) also removes its settings and per-post analysis data (score, report, action plan) from the database, instead of leaving it behind indefinitely.
  • Fixed: the FAQ claimed Rank Math support, but the meta description check and Quick Fixes only ever read Yoast’s and AIOSEO’s fields — Rank Math’s actual field (rank_math_description) was never checked. Worse, Quick Fixes’ save logic was a two-way Yoast/AIOSEO guess that defaulted to writing into Yoast’s field whenever AIOSEO wasn’t active — including when Rank Math was the only SEO plugin actually installed, silently saving into a field nothing would ever read. Both the read and save paths now correctly detect and use whichever SEO plugin (Yoast, Rank Math, or AIOSEO) is actually active, falling back to the post excerpt when none are.
  • Fixed: skipped pages (utility pages the classifier intentionally excludes from scoring, stored as score -1) were being pulled into the REST site-summary and the emailed report as if they were real scores — dragging down the average, permanently setting the lowest score to -1, and miscategorizing them as “Needs Work” in the email. Both now exclude skipped pages from every bucket and the average, matching how the main dashboard already handles them, and track them as their own distinct count instead.
  • Fixed: two admin-side rendering functions (the post-editor meta box update and the bulk-analyze results list) built their HTML by concatenating values — including a post’s own title — directly into strings inserted via jQuery’s .html(). A post title is freely settable by any Contributor or Author, so this was a real stored-XSS path: a crafted title could execute in an administrator’s browser when they viewed the dashboard or bulk-analyzed their content. Both functions now build the DOM safely (every dynamic value via .text(), matching the pattern already used correctly in the Quick Fixes screen).
  • Changed: softened the Quick Fixes URL-slug wording, which called a slug change unconditionally “safe” because WordPress redirects the old URL. That’s true for the URL itself, but doesn’t cover external links, caches, custom redirect setups, or third-party SEO tooling tied to the old URL — the copy now says so plainly instead of asserting blanket safety.
  • Changed: caught two more absolute-sounding claims that slipped through the earlier language-softening pass (“give AI richer context,” “helps AI map content sections”) and reworded them to match the “can help” framing used consistently everywhere else.
  • Fixed: a third, independent copy of the meta description lookup — inside the action-plan generator this time — still only checked Yoast and AIOSEO, missed when the check itself and Quick Fixes were fixed for Rank Math. A Rank Math user could pass the Meta Description check but get an Action Plan built from the wrong (empty) value. Now routes through the same shared helper as the other two, so this can’t drift out of sync again.
  • Changed: added wp_unslash() before sanitizing $_GET values (page, view, order, paged, post_id), per WordPress coding standards.
  • Changed: reworded the one remaining “AI-friendly schema types” reference in the readme to “Relevant structured-data types,” matching the neutral positioning used everywhere else.
  • Fixed: a full pass against the official WordPress.org Plugin Check tool. Added missing “translators:” comments throughout (required wherever a translatable string contains a placeholder, so translators know what each one means); numbered multi-placeholder strings correctly (e.g. %1$s/%2$d) so translators can reorder them per their language’s grammar; escaped several previously-unescaped dynamic values in the dashboard, Action Plan, and emailed report (a threshold value, breakdown-bar percentages, several counts, a category color); fixed two spots (Title Length and Content Length details) where part of the message was a raw, untranslated PHP string instead of going through a translation function; converted three multi-line strings built via concatenation into single literals (concatenation isn’t parseable by WordPress’s translation-extraction tools); and replaced a timezone-unsensitive date() call with gmdate(). A few remaining Plugin Check notices are left as-is with reasoning: read-only GET parameters for admin-screen navigation (view/sort/page state, not a state-changing action) don’t need nonce verification; querying and sorting by post meta is inherent to how this plugin stores and compares scores; a VIP-tier advisory about a small bounded exclude() query; and two flagged hook/array-key patterns that are false positives (WordPress’s own ‘the_content’ core filter, and a plain data array that happens to use the string “meta_key” as a key name rather than an actual database query parameter).
  • Fixed: a second Plugin Check pass caught what the tool couldn’t verify from the first — printf()/sprintf() arguments substituted into an already-escaped template still need to be individually wrapped (e.g. absint() for a %d placeholder), since static analysis can’t confirm a substituted value is safe just because the surrounding template went through esc_html__(). Also: a translators comment must sit directly above the actual translation function call, not above a wrapping printf() one line up, for multi-line calls; one internal-links count genuinely missed a translators comment in the first pass; and one spot with intentional HTML in a translatable string (a hardcoded <strong> tag around an already-escaped value) is marked with a justified phpcs:ignore rather than escaped in a way that would break the formatting.
  • Changed: a third Plugin Check pass came back with zero remaining errors — only the 20 warnings already identified and explained in earlier notes (read-only GET navigation params, meta-key/meta-query queries inherent to how this plugin stores and compares scores, four false-positive array-key matches, and one VIP-tier advisory on a small bounded query). Each is now marked with an explicit, justified phpcs:ignore comment explaining why it’s safe, so Plugin Check reports a clean pass rather than requiring a reviewer to take the earlier written explanation on faith.
  • Fixed: Bulk Analyze didn’t distinguish a genuinely low score from a skipped page’s placeholder -1 score, unlike the dashboard, email report, and site summary (all fixed earlier this cycle) — a skipped utility page could drag down the average and show up under “Pages Needing Attention” with a score of -1. The REST response now reports each result’s skipped state, and Bulk Analyze excludes skipped pages from every statistic, showing them as their own honest count instead. The same -1-as-a-real-score gap existed in two smaller places too: the list-table score badge and the post-editor meta box both now show a neutral “Skip” indicator instead of styling -1 as a failing score.
  • Fixed: Quick Fixes applied a change (meta description, categories/tags, slug) and removed it from the list, but the post’s cached score/report weren’t refreshed — update_post_meta() and the taxonomy functions Quick Fixes uses don’t trigger save_post at all, so the only path that could auto-refresh (the site’s general “auto analyze on save” setting) didn’t apply, and was unreliable even when it did apply (that setting can be off, or restricted to certain post types). Each of those three fixes now explicitly re-analyzes the post right after saving, independent of that setting, so the score is accurate immediately. Alt text intentionally isn’t auto-reanalyzed — an image can appear on more than one page, and there’s no reliable way to know which ones without an expensive site-wide scan; the affected page(s) pick up the change on their next regular re-analysis instead.
  • Changed: reduced the timeout on both frontend-fetch calls (content resolution: 15s 8s; structured-data detection: 10s 6s). Both can run synchronously during save_post when “Auto Analyze” is on (the default), and can both fire in the same request for some page types — worst-case combined blocking time is now 14s instead of 25s. This wasn’t independently verified against real hosting conditions (loopback request behavior varies a lot by host/security-plugin setup) — recommend testing directly before relying on it: publish/update both a normal post and a page-builder page, with the site publicly reachable and with any caching/security plugins active, and confirm the editor doesn’t hang uncomfortably. If it’s still a problem on some hosts, the more thorough fix is making auto-analysis asynchronous instead of running inline during save_post — a larger change, and one to make once there’s a concrete case showing it’s needed rather than speculatively now.
  • Changed: removed flush_rewrite_rules() from activation and the entire deactivation hook (which existed only to call it). This plugin registers no custom post types or rewrite rules, so there was nothing to flush.
  • Changed: rewrote the plugin’s short description, main description, and header description to lead with the honest causality up front, rather than leaving it as an implication: this plugin doesn’t get content found by AI search, the content itself does — the plugin checks it against a framework and shows what to fix. Also added a dedicated FAQ entry (“Will this plugin get my content found by AI search?”) answering this directly, since it’s an easy thing to misread from a name like “AI Search Ready” and better to state plainly than leave to inference.
  • Fixed: the Taxonomy Usage check called wp_get_post_categories()/wp_get_post_tags() unconditionally, with no check for whether the post type actually supports either taxonomy. WordPress core Pages don’t support categories or tags at all by default — so with the plugin’s default settings (which analyze both posts and pages), every single Page on every site would always fail this check, permanently, regardless of anything the user did, since there’s no field to even set it in. The check now only runs when the post type actually supports at least one of the two taxonomies. A second, related bug caught in review: even after that fix, the check still required BOTH a category AND a tag to pass, so a custom post type registered with only one of the two (a common setup) could still never pass — and the failing-case guidance in the Action Plan told users to add tags to post types that have no tag field at all. It now requires only whichever taxonomy the post type actually supports, and excludes the site’s default “Uncategorized” category from counting as real categorization (it’s auto-assigned to every post, so counting it made the check trivially pass for anything that supports categories, regardless of whether the user did anything) — matching logic Quick Fixes’ categories/tags list already used correctly.
  • Fixed: the URL Readability check flagged a short or generic slug (e.g. “home”) on the site’s designated static front page — but WordPress always serves that page at the site root regardless of its own slug, so the slug has zero effect on the real, live URL. There was nothing to actually fix, just a cosmetic internal field. Both the check and Quick Fixes’ slug list now skip the static front page entirely.
  • Changed: the Meta Descriptions section in Quick Fixes listed pages with a genuinely non-empty, visible description with no indication of why they were still listed (length outside the 120-160 target), which could look like the page had been misread as blank. Each field now shows a live, updating character count with the actual reason underneath (“87 characters — below the 120-160 target”), and the section’s own description spells out that a real description can still appear here if it’s the wrong length.
  • Fixed: the URL Readability check’s 2-8 word rule doesn’t fit a small set of universal, conventional single-word page slugs — “about”, “contact”, “pricing”, “services”, and similar. These are recognizable and expected precisely because they’re short; flagging them as needing to be “more descriptive” would mean recommending against genuinely standard, good practice, not fixing a real gap. Both the check and Quick Fixes’ slug list now recognize a short whitelist of these conventional single-word slugs and no longer flag them.
  • Fixed: the Image Alt Text check matched every raw tag in a page’s rendered content with no filtering, including invisible tracking/analytics pixels injected by third-party widgets (booking tools, CRMs, marketing scripts) — a 1×1, display:none image with no visual content, existing purely as an analytics beacon. Flagging these for “descriptive alt text” doesn’t make sense; there’s nothing to describe. The check now excludes any image matching that pattern (1×1 dimensions, or explicitly hidden via inline display:none) before counting or requiring alt text.
  • Fixed: the “Add OpenGraph meta tags” check was checking the wrong things — Yoast’s own internal action name (wpseo_head, not a reliable signal of anything) and a per-post OG title override meta field that’s only set if the user manually typed a custom OpenGraph title in, which normal use of Yoast, Rank Math, or AIOSEO never requires, since all three auto-generate full OpenGraph tags (title, description, type, url, site name) the moment they’re active. The check now looks for whether one of those plugins is active, which is what actually determines whether OpenGraph tags are being output. A more accurate version would fetch the live page and read the actual og: tags rather than inferring from plugin presence (a plugin can have its social-meta feature turned off) — deliberately not done in 1.2.0, since it would add a third live-page fetch on top of the two content resolution and structured-data detection already make during save_post when Auto Analyze is on. Worth revisiting in a later version.
  • Fixed: two Quick Fixes (the slug fix, and the meta description fix when no SEO plugin is active) use wp_update_post(), which fires WordPress’s own save_post action — triggering a full auto-analysis if that setting is on, in addition to the analysis Quick Fixes already explicitly runs right after applying the fix. One click could result in two full analyses back to back, each potentially making its own live-page fetches. The auto-analyze handler now checks a flag Quick Fixes sets around its own internal updates and skips itself when it’s set, since Quick Fixes’ own explicit re-analysis always runs regardless.
  • Fixed: the Bulk Analyze results screen only ever listed and linked to pages scoring below 60 (“Pages Needing Attention”) — any page scoring 60 or higher, including a perfect page with nothing to fix, had no listing and no way to reach its Action Plan from that screen at all, even though visiting it directly worked fine and correctly showed a “No Actions Needed” confirmation. Every scored page now gets a link somewhere on that screen — low scorers stay in the always-visible list, everything else is in a collapsed “Other Pages” section so a large site’s full list of already-fine pages doesn’t crowd out what actually needs attention.
  • Fixed: analyze_post() resolves a post’s content once (page-builder aware, with a live-page-fetch fallback), then generated its Action Plan — which created its own separate Content Resolver and resolved the exact same post a second time. On a page-builder site where resolution falls back to fetching the live page, this meant two live-page fetches for one analysis instead of one, on top of whatever else the analysis was already fetching. Action Plan generation now accepts the already-resolved content as an optional parameter and reuses it when available, only resolving on its own when generated independently of a fresh analysis (e.g. regenerating from a cached report with no resolved content to reuse). Reduces the worst-case synchronous fetch chain during save_post from roughly 22 seconds to about 14.
  • Verified SSL when fetching frontend content for page-builder analysis
  • Clarified scoring language throughout readme, dashboard, and email reports: scores reflect Fifteenth Labs AI Search Ready’s own analysis framework, not a live measurement of how ChatGPT, Perplexity, or other AI systems will actually treat content
  • Verified against WordPress 7.1 through live functional testing across multiple post types (posts, pages, and a custom post type) — Dashboard, Action Plan, Re-Analyze, and Quick Fixes all confirmed working correctly.
  • Softened absolute claims in recommendation copy (Action Plan, Analyzer, Content Quality, Structured Data checks) — e.g. “AI needs X” now reads as “X can improve machine readability,” reflecting best-practice guidance rather than asserted facts about how AI systems work
  • Aligned OpenGraph messaging to consistent, neutral wording across dashboard and action plan; removed a readme sentence that stated a claim and disclaimed it in the same breath
  • Completed a full pass across all recommendation copy (word count, readability, topic focus, headings, paragraphs, internal links, alt text, title length, URL slugs, anchor links, content freshness, taxonomy, author E-E-A-T) to remove remaining absolute claims about how AI systems operate, replacing them with guidance framed around this plugin’s own scoring methodology
  • Softened the main plugin description, readme FAQ answer, and two internal file-header comments that still described the plugin as helping “AI systems understand/extract/cite” content
  • Removed remaining AI assertions from meta description, anchor links, JavaScript/crawler, heading, and paragraph structure copy, plus an internal code comment, so recommendations describe content structure benefits without asserting how AI crawlers specifically behave
  • Softened four more recommendation strings (lists, title keywords, FAQ detection, meta description length) that referenced “AI extraction” language

1.1.0

  • Added page builder content resolver (BeTheme/Be Builder, Elementor, Divi, WPBakery, Beaver Builder, Oxygen, Brizy)
  • Added “Getting There” (60-79) status category alongside Needs Work and AI Ready
  • Added visual breakdown bar on dashboard showing all 3 score buckets
  • Added full Action Plan system with ADD/REMOVE/CHANGE instructions per page
  • Added Email Report with status picker (choose which statuses to include)
  • Added HTML download and PDF download for reports
  • Added diagnostic REST endpoint for troubleshooting content detection
  • Added “Back to Dashboard” button on Action Plan pages
  • Added version badge in dashboard header
  • Fixed script loading on all plugin pages
  • Fixed score bucket math so all numbers add up to total analyzed

1.0.0

  • Initial release
  • 5-category analysis system with 30+ individual checks
  • Dashboard, bulk analysis, and per-post meta box
  • REST API endpoints
  • Auto-analyze on publish