● Product changelog

What changed, and when

Every release across the whole platform — the schema tools, the audit tools, the performance tools, the content and AI-visibility tools, the learning hub and the admin panels. Newest first. Each release gives the task it set out to do, which tools it touched, a summary of what happened, and every individual edit. Where something was reporting incorrectly it says so, and says what it does now. The actual code changes are at the bottom of the page.

Currently running v2.6.0 · Jump to code changes

v2.6.0

Task Correct the audit rules that were reporting faults which were not there, and rebuild the usage statistics.

ToolsSite AuditSite Audit ProAccessibility CheckerImage OptimisationSchema DebuggerReadability CheckerTool DirectoryAdmin statistics

Summary

Eleven rules were producing false findings. That matters more than a missing feature: people use these reports to decide what to change on their sites, so a rule that invents a problem costs them real work, and a rule that misses one costs them rankings. Every correction below was made in both places the rules live — the server module and the copy that runs in the browser — because fixing one leaves the fault live in the other.

The social profile checks needed a different kind of fix. Several large platforms refuse automated requests outright, and the audit was treating a refusal as proof the profile was broken. It cannot tell the difference from the outside, so it now says so rather than guessing, while still reporting genuine failures from platforms that do answer.

The usage statistics were wrong in both directions at once, which is why the numbers had never looked credible: some tools were never counted at all, and others were counted many times per run.

Edits

Audit rules

  • Links without an href — false count. The pattern matched any tag starting with the letter A, so <article>, <address>, <aside>, <abbr> and <area> were counted as broken links. A page with two articles and an address block was told it had three faults it did not have. Now requires a word boundary, so it matches anchors only.
  • Legacy image format — ignored <picture>. The rule read the fallback <img src> and reported PNG even when a WebP or AVIF source was offered above it. Now checks the parent element for a modern source before flagging.
  • Lazy loading recommended for the LCP image. Every image was flagged, including the hero. Lazy-loading that image delays the largest contentful paint. The first image, and anything marked fetchpriority="high", is now skipped.
  • Long sentences undercounted. The count was taken after the list was cut to eight for display, so eleven long sentences reported as eight. The full count is taken first and sent separately from the examples.
  • Duplicate “click here” warnings. The plain-English layer matched text inside each finding's own “Learn more” link, so two schema recommendations were relabelled as two identical generic-link warnings. Help links are stripped before matching, and the pattern narrowed to what the link rule actually emits.
  • Raw HTML shown as text. Two schema messages carried an anchor tag inside the message string, which the display layer escaped for safety, printing the tags on screen. The markup was removed from the messages; the escaping stays.

Brand and social presence

  • Blocked platforms reported as broken profiles. A 403, a 429, or a redirect to a login page was shown as a red error at the top of the report. Those platforms, and the status codes meaning “blocked” (401, 403, 405, 429, 451, 999), now produce an informational “could not be verified automatically” notice. A genuine 404 from a platform that answers bots is still an error.
  • Threads not detected. The domain list held threads.net only, so profiles on threads.com — the current domain — were reported as verified but not linked, when they were linked.
  • Google Business Profile not detected. Google's own share button produces share.google and maps.app.goo.gl links, neither of which was in the list. Both added, with goo.gl/maps and business.site.
  • “You're not on any major social platforms” removed. It replaced an accurate count of missing platforms with a claim that was false for anyone with some but not all, then listed platforms the customer already had among the things to claim.

Contact detection

  • Contact details reported absent when present. The upstream provider returns an empty contacts field for most sites, even when its own page extract contains the email address. The page is now read directly for mailto links, plain addresses, telephone links and social profiles, with share and intent links excluded. On our own homepage this went from nothing found to one address and eight profiles.

Usage statistics

  • Counted wrongly in both directions. The endpoint map listed three routes that do not exist, so most tools never registered a run, while geo-grid audits counted every progress poll and parallel sub-request separately. One day's real figure of 31 runs displayed as 1,252.
  • Counting rebuilt. Forty-one real endpoints mapped; free and member versions of a tool collapse to one entry; polling, job status, report fetches, credit checks and authentication excluded; and repeat requests from one visitor to one tool within five minutes count as a single run. Log entries are de-duplicated first, since the server writes each request to two files.

This site

  • Accessibility. A main landmark and skip link added, so keyboard and screen-reader users can pass the navigation.
  • Hero image. Converted to WebP — 94 KB to 20 KB, 79% smaller at 46.8 dB — served with a PNG fallback and given explicit dimensions to stop layout shift.
  • Readability. Twelve over-long sentences split; long-sentence count from eleven to zero.
  • Meta description. The homepage description opened with an apology rather than saying what the site does. Replaced in all eight places it appeared — two meta tags and six structured-data fields.
  • Tool directory layout. Two-column groups now collapse to one where there is no room, and the demo and guide links wrap rather than forcing a fixed width.

v2.5.0

Task Add the Google Maps layer to geo-grid audits, make generated schema claim the page properly, and make the audits usable on a phone.

ToolsGeo-Grid AuditSchema Markup BuilderAI Schema GeneratorSite AuditAudit toolsPerformance toolsContent toolsSchema toolsAdmin statistics

Summary

Geo-grid audits measured organic rankings across a grid of points, but local searches are won in the map pack, which was not being measured at all. Every point now runs a Maps query alongside the organic one, and the results plot on a real map with a competitor table. The field list came from reading an actual API response rather than the documentation, which showed a dozen populated fields being discarded — including Google's own stated reason for why each business ranks where it does.

On the schema side, three faults were undermining the output. Every primary node was being emitted with nothing pointing at it, so a parser saw an orphan and the page gained no authority for the type it declared. Product pages were detecting as blog posts because two major platforms wrap products in an article tag. And the page-type label was reading the customer's own previously published schema rather than the server's decision — so for anyone who had used the generator before, it was reporting its own last answer back at them.

The mobile fault was the most severe of the three: no logged-out visitor on a phone could run any audit at all, on any of the four tool pages.

Edits

Added

  • Geo-grid Maps layer. A Maps query at every grid point alongside the organic one, storing up to twenty businesses per point with thirty-two fields each. The report returns per-business aggregates — average position, coverage, points held outright, distance, rating and category.
  • Fifteen further data fields after a live response showed them being discarded: telephone, claimed status, photographs, opening hours, categories, postcode, and the ranking justifications.
  • Map and competitor table. Real coordinates, with a top-six competitor table showing ratings, distances and categories. Self-hosted, because the site's content security policy blocks external script sources.
  • Administrator statistics panel — visitors by day, week, month and rolling window; bots listed individually; tool usage with session counts and dwell time; top pages; referrers. Regenerated every fifteen minutes.
  • Crawler verification script that fetches any URL as seven different agents and reports what each receives.

Fixed

  • Schema type enforced server-side. The type list was restricted in the browser, but the endpoint still accepted any type from any caller — hiding an option changes what the browser offers, not what the server accepts. A permitted-list check now covers all nine generation routes including the free one. Administrators are exempt; a logged-out caller reads as not-administrator and is refused.
  • Orphaned schema nodes. The WebPage now names the primary node as its main entity. Placement mattered: an earlier attempt referenced an identifier that a later de-duplication pass deleted, producing a reference to nothing.
  • Product pages detected as blog posts. Detection triggered on any article element, and its guard only checked existing schema — which is empty on precisely the pages people bring to a generator. The tag trigger was removed; explicit publisher signals are still honoured.
  • Page-type label read its own output. It now takes the server's decision first.
  • Contradictory geo-grid banner. It said you did not rank in the top hundred while the panel below reported an average position of nine. Both were true — one organic, one local pack — but it read as broken. The banner names which is which, with the Maps position shown separately.
  • Geo-grid time estimates. The Maps call roughly doubled the work per point, so the quoted one, three and six minutes became two to five, six to fourteen, and twelve to twenty-seven.
  • Mobile URL entry — 33 inputs. Validation ran only when a field lost focus, which on a phone frequently never happens, so the run button stayed disabled. None of the thirty-three inputs had an Enter handler either.
  • Dead first tap. Validators set the URL invalid before their asynchronous check began, so tapping Run blurred the field, restarted validation and disabled the button mid-tap. An unchanged URL is no longer re-validated.

Changed

  • Maps failure degrades rather than fails — the audit returns organic results instead of failing outright.
  • Homepage hero image added to the header, marked as the largest contentful element.

v2.4.0

Task Overhaul schema detection and the enrichment panel.

ToolsAI Schema GeneratorSchema Markup BuilderSchema DebuggerAEO / AI Visibility CheckerHomepage

Summary

Detection was rebuilt for FAQ, HowTo, Article and SoftwareApplication pages, and the enrichment panel gained the ability to prefill from the page and write corrections back into the generated markup. The most consequential finding was not a feature at all: a flag in the field-setting logic was silently overwriting genuine values read from the page, so information the generator had correctly extracted never reached the output. Nothing reported an error — the data simply disappeared.

Edits

Fixed

  • Field-setting flag wiping real page data. Correctly extracted values were being overwritten before output, invisibly.
  • Article rows matched by position. Rows are matched by headline now, so editing one article's details no longer applies them to another.
  • Dates and author details. Dates emitted in ISO form; author profile URLs included rather than dropped.
  • Author profiles overwritten. Multiple profile links for a person are merged rather than replaced.

Added

  • Detection rebuilt for FAQ, HowTo, Article and SoftwareApplication pages.
  • Enrichment panel prefill, filling from the page and applying corrections back into the markup.
  • Plan pricing emitted as an aggregate offer rather than a single price.
  • Images emitted as proper image objects rather than bare URLs.
  • Homepage questions, articles and sharing. A genuine FAQ section and article listings were added to the homepage, with social sharing in the footer, so the structured data describes content that is actually on the page rather than asserted about it.
  • Answer-engine tooling updated alongside the generator changes, keeping the content tools consistent with the new detection.

v2.3.0

Task Add comments to every tool, track free usage, and rewrite the keyword tool.

ToolsAll tool pagesKeyword Analysis & GapAdmin free-usage

Summary

Questions about a tool were arriving by email, detached from the thing they were about. Comments now sit on each tool page with one level of threading, styled for both themes, with moderation behind them. Free tool usage is logged per visitor, which required correcting how the visitor's address was read — the server passes it in a different header than the code expected, so every free run had been recorded against the proxy rather than the person.

Edits

Added

  • Comments, per tool — one level of threading, avatars, light and dark themes, collapsible section and collapsible replies.
  • Moderation with administrator approval and auto-approve, and a spam trap that no longer catches genuine visitors whose browsers autofill hidden fields.
  • Free usage tracking per visitor, enriched with network information.

Fixed

  • Every free run attributed to the proxy. Corrected to read the header the server actually sets.
  • Keyword tool rewritten to extract terms from the page itself rather than an external lookup.

v2.2.0

Task Verify the specification behind the AI discovery files, and rebuild on real authorities.

ToolsLLMs.txt GeneratorAI permission filesRobots & Sitemap TesterLLMs.txt AuditorSitemap

Summary

The platform had been generating a bundle of AI discovery files to a specification presented as an emerging standard. Reading its own published pages carefully showed something else. It is published by a digital agency rather than a standards body; its governance page states plainly that no standards-body engagement is currently active; and every file in the bundle is a borrowed convention, with the original contribution being the bundling — a numbering scheme, tiers, a validator and a registry. The same organisation writes the specification, certifies conformance to it, and sells directory listings, certification badges and quarterly re-checks.

None of that is illegal. The licensing genuinely permits free use and nothing controls the filenames, so the objection was product direction rather than law: building on it meant tracking one vendor's release cycle while describing it to customers as a standard.

Every reference was removed — generators, live files, sitemap entries, audit checks and the marketing copy claiming search engines already consumed these files. What replaced it stands on three things that hold up: Schema.org, which search engines genuinely parse today; llmstxt.org, the actual origin of llms.txt; and the IETF working group doing the real standards work on AI usage preferences, chaired by the author of the robots.txt standard.

Edits

Removed

  • ai-catalog.json — both copies deleted, belonging to a third commercial specification, along with its generator, button, instruction text and sitemap entry.
  • agents.md — removed as out of scope. A legitimate open format backed by the Linux Foundation, but it guides AI coding agents around a code repository, which has nothing to do with search. Removed from the generator, the related-files list, the live site and the sitemap.
  • Specification headers stripped from the live ai.txt and the generator that writes it.
  • Two audit checks tied to the commercial specifications, with its labels stripped from every remaining check title and piece of advice.
  • False marketing copy — claims that one search engine already fetched identity.json and another had shipped support for the catalogue file. Neither was true.

Rebuilt

  • identity.json to pure Schema.org, with a nine-option organisation-type selector driving the type. The live file was replaced and validated, a stray duplicate deleted, and the wrong upload path that produced it corrected in two places.
  • ai.json became ai-permissions.json — our own format rather than validating against someone else's schema URL, with permissions mirroring the ai.txt output so the two cannot contradict.
  • llms.txt gained About, Services and Contact sections, using real site data only, confirmed to parse with the reference implementation's own parser.
  • Sitemap trailing-slash bug fixed, where subdirectory index pages produced redirects.

Added

  • An authority configuration file naming the two genuine sources.
  • A monthly authority watcher that fingerprints the published specifications and reports when they change, so the platform follows the standards bodies rather than a vendor's announcements.
  • Content-Usage parsing in the robots.txt tester — the draft standard's way of expressing whether content may be used for AI training or search. Sites carrying it score full marks; sites without lose three points and get an explanation covering the syntax, the draft's status, and its own caveat that these preferences express a wish and compel nothing.

Considered and declined

  • A content-signal format promoted by a large infrastructure provider was reviewed and not adopted. It is one company's convention rather than a standards-body output, and adopting it would have repeated the mistake this release exists to correct.
  • A separate Content-Usage generator was scoped and closed as unnecessary — the tester's own fix text already gives the exact line to add.

v2.1.0

Task Expand the thin guide and demonstration pages, and correct what they claim.

ToolsGuidesDemo pagesLearning Hub

Summary

Fifty-four pages were rewritten from roughly four hundred words to over two thousand — twenty-six guides, completing that family, and twenty-eight demonstration pages. Each gained teaching sections and six or seven genuine questions and answers, mirrored into the structured data so the markup matches what is actually visible.

The larger part of the work was editorial rather than technical. A great deal of the existing content asserted SEO folklore as fact, and correcting it took more judgement than adding words. Two pages were also found genuinely broken on the live site, by scanning every file rather than reading them.

Edits

Content corrected — claims that were not true

  • Redirect chains diluting link equity
  • Temporary redirects not passing equity
  • Multiple headings confusing search engines
  • A duplicate content penalty
  • A penalty for inconsistent business details
  • Keyword density targets
  • Readability as a ranking factor
  • Click-through rate as a direct ranking signal
  • The need to disavow toxic backlinks
  • FAQ and how-to rich results as still widely shown
  • Structured data volume driving AI citations
  • Author profiles conferring expertise signals
  • Generic anchor text wasting equity
  • A hundred-link-per-page limit
  • Business posts and photographs as ranking factors
  • Virtual offices and listing suspension risk

Fixed — found live on the site

  • Unescaped quotation marks in a demonstration page had broken its meta description, its social description and its structured data, which failed to parse entirely.
  • Template artefacts removed from three files. Three others flagged by the same search were genuine framework syntax and correctly left alone.
  • Structured-data drift in two completed pages, where a rewritten visible answer had not been carried into the markup.
  • One page under the word floor at 1,906 words, extended to 2,149.

Where the programme stands

  • Fifty-four pages of two hundred and fifty-three are complete, across two batches — an initial fifteen, then a further thirty-nine. The twenty-six guide pages are finished as a family; twenty-eight of the demonstration pages are done.
  • Remaining: roughly thirty-one demonstration pages, twenty-five beginner tutorials, forty-one how-to pages and twenty-seven schema-generator pages, the last of which are the hardest and are deliberately left until the method is proven.
  • One page is still broken and known. A tutorial page carries the same unescaped-quotation fault described above and has not yet been corrected. It is recorded here rather than quietly fixed later, because it is live.

v2.0.0

Task Stop the generator emitting markup the page does not support, and make the item-count control work.

ToolsAI Schema GeneratorSchema Markup BuilderRich Snippets SuiteHomepageLanding pages

Summary

Two faults were producing markup that did not describe the page. An ItemList was being added to every page regardless, and where no list existed the generator built a single fabricated item out of the page title — markup asserting something untrue, which is the fastest way to lose rich results. Recipe was being detected whenever the words “recipe” or “ingredient” appeared anywhere in the text.

Separately, the item-count control had never worked. The cap was fixed per route rather than read from the request, and a second hard limit of five applied to questions and answers regardless of selection. That is the actual cause of the long-standing complaint that choosing twenty-five returned five, or that the count appeared stuck.

Edits

Fixed

  • ItemList emitted on every page, with a fabricated item built from the page title where no list existed. Now requires at least two genuine list items; the title fallback is gone.
  • Item count ignored your selection. The cap was fixed per route rather than read from the request. The selected count now governs every type.
  • Questions and answers capped at five by a separate hard limit regardless of selection.
  • Panel showed the wrong count, displaying whichever list was longer — the generated one, correctly capped, or the page's own uncapped set.
  • Recipe misdetection on any page mentioning recipes or ingredients. Real recipe markup is now required.
  • Rows labelled “Article” regardless of type. Blog posts and news articles are named as such.

Added

  • Fourteen schema types opened to all users — local business, software application, article, news article, FAQ page, how-to, website, web page, product, organisation, person and item list among them. Administrators retain the full set.
  • A new homepage, rebuilt in the site theme and focused on what the tools actually do: the three main actions inline at the top with duplicates removed, the thousand-item limit attributed to schema generation only rather than implied across everything, the debugger described accurately as checking schema that already exists, llms.txt promoted to a first-class feature, an explanation of type overriding, the ninety-plus schema types grouped into six categories, and both pricing routes stated plainly.
  • A dedicated schema and llms.txt page for the AEO subject, built as a duplicate of the homepage with only the title, description, canonical, social URL, page schema and heading changed, so the two do not compete on layout while targeting different queries.

v1.9.0

Task Add the llms.txt context files, and audit the AI permission checks.

ToolsLLMs.txt GeneratorLLMs.txt AuditorContent toolsSitemap capping

Summary

Where llms.txt is an index of your pages, the context files carry the content itself: every linked page fetched, converted to markdown, and wrapped in the structure models expect. No AI call is involved — it is a mechanical expansion, so the free version builds automatically after generation, and only the full one, which includes optional sections, is charged.

Auditing the existing checks turned up something worse than a missing feature. The test for a required section in AI permission files was written in a way that matched a single character from the section name rather than the section name itself, so it passed almost any text at all. A file reading “hello there, nothing here” scored as valid on three separate checks. The check had been validating nothing since it was written.

Edits

Added

  • llms-ctx.txt and llms-ctx-full.txt, the free one building automatically after generation, the full one charged by size.
  • An HTML-to-markdown converter, stripping scripts, styles, navigation, headers, footers and forms so the output is content rather than page furniture.
  • A link-count control from five up to no limit, mirroring the schema generator's tiers, with the request value authoritative.

Fixed

  • The identity check validated almost anything. It now requires the real section header.
  • ai.txt written in the wrong format. It emitted a structure that could not pass the specification it claimed to follow, and invented a contact URL. Rewritten to the correct sections, with no contact line unless you supply one.
  • llms.txt section ordering. Descriptive prose was written as headed sections, when the specification requires prose above the first heading and headed sections to contain file lists only, with the optional section last.
  • Sitemaps skipped entirely. A sitemap of over eleven hundred URLs is now sliced to the count you choose, as lists and products are, and the output states the true total alongside the number shown.
  • Credit balance did not refresh. Two routes returned the updated balance but nothing applied it, so the figure only changed on reload.
  • Price labels unreadable — inline styles were overriding the class, rendering cost chips at eleven pixels in a generic monospace face.
  • Interface text leaking into output, with cost indicators appearing inside generated headings.
  • Panel copy out of date. The download panel still said three files when there were five, listed only three upload paths, and described ai.txt as following a permissions standard it does not. Corrected on all three counts.

v1.8.0

Task Support question and answer pages end to end.

ToolsAI Schema GeneratorSchema DebuggerSchema Markup BuilderSchema tools panel

Summary

QAPage joined the supported types across detection, generation, the manual builder, the type list and the debugger. Most of the work was not the type itself but the answer text. Question pages are dense with interface — voting widgets, comment counts, filters, author badges, reputation scores — and a naive read picks up any of it. The generator now sources the answer in a defined order, rejects candidates that merely repeat the question or turn out to be stylesheet code, and strips the surrounding furniture before reading. Where no readable question exists it emits a plain WebPage rather than invalid markup.

Edits

Added

  • QAPage end to end — detection, node builder, dropdown, URL-based generation, manual builder, and the enrichment panel with its own description and rich-result explanation.
  • Debugger rule for QAPage, validating the main entity as a single question rather than a list, as the specification requires.
  • Collapsible enrichment panel — sections start closed, open where real page data was found, and carry a badge showing what was found and what was not.

Fixed — getting the answer right

  • Answer sourcing order — existing structured data, then page body, then markdown.
  • Site-agnostic selectors rather than assuming one site's markup conventions.
  • Prose guard rejecting stylesheet or code content picked up as answer text.
  • Distinct-answer guard rejecting candidates that repeat the question, preferring the longest genuine prose.
  • Interface stripping — buttons, forms, voting widgets, comment counts, filters, promotional blocks and page headers removed before reading.
  • Byline stripping — author, profile, avatar, badge and reputation elements removed.
  • Required-field gate — a plain WebPage where no readable question exists, rather than invalid QAPage markup.
  • Event mining suppressed on question, FAQ and forum pages unless a genuine event signal is present.
  • Product selector widened so a genuinely listed product is captured, requiring a name, with price read from a currency symbol.
  • Fourteen page titles brought into range. Titles outside roughly fifty to sixty characters are truncated or padded in search results. A script rewrote them and updated both the title tag and the page's own metadata, backing up each file first.

v1.7.0

Task Merge the how-to-fix pages into the learning hub as searchable cards.

ToolsLearning HubError Docs

Summary

Four hundred and fifty-four how-to-fix pages existed but were only reachable if you knew the URL. They are now searchable cards in the learning hub, bringing it to 801 in total, so an issue in an audit report links to the specific repair rather than a definition. The pages had been written in three different layouts over time, so the extractor reads all three, with a conditional pass for pages that mix them.

Edits

Added

  • 454 pages merged as searchable cards, bringing the hub to 801, averaging nearly nine steps per card. None skipped, none thin.
  • An extractor reading three page layouts — step blocks with preformatted code, numbered headings, and ordered lists under a step-by-step heading.

Fixed

  • Twenty-three thin cards. On mixed-layout pages the extractor found two step blocks and ignored the headings and code holding most of the content. A conditional second pass fires only when the first is thin, with overlap removal so clean pages are not double-counted. Thin cards went from twenty-three to zero.
  • Thirty-eight mislabelled cards — thirty-seven with no type, one miscategorised — which had been rendering with an undefined label.
  • Escaping at the injection points. The escape helper existed only inside the modal, so grid rendering would have failed on any card whose title or summary contained markup.
  • Search routing verified across all thirty-one tool keywords: twenty-seven resolve to a matching card. One was fixed by adding the plain tool key to eleven platform-specific pages. The remaining four have no fix page written yet — a content gap, not a broken link.

Also produced

  • Two product walkthrough videos — a twenty-nine slide piece covering the schema generator, and an eleven slide piece on the llms.txt auditor, each built from real screenshots of the tools rather than mock-ups.
  • Listing and social copy to accompany them.

Code changes

The actual edits behind the releases above, before and after. Where a rule exists in both the server module and the browser copy, both were changed — fixing one leaves the fault live in the other.

v2.6.0

Links without an href

lib/accessibility-checker.js + audit-tools.html

No word boundary after the tag name, so the pattern matched any element starting with the letter A.

Before
/<a(?![^>]*href)/gi
After
/<a\b(?![^>]*\bhref\b)/gi
v2.6.0

Legacy format ignored <picture>

lib/image-optimisation-checker.js + audit-tools.html

The rule read the fallback img and never looked for a modern source alongside it.

Before
if (/\.(png|jpe?g)(\?|$)/i.test(src)) {
  issues.push({ type:'warning', label:'Legacy image format' });
}
After
function hasModernSource(img) {
  const parent = img.parentElement;
  if (!parent || parent.tagName !== 'PICTURE') return false;
  return [...parent.querySelectorAll('source')]
    .some(s => /image\/(webp|avif)/i.test(s.getAttribute('type') || ''));
}

if (/\.(png|jpe?g)(\?|$)/i.test(src) && !hasModernSource(img)) {
  issues.push({ type:'warning', label:'Legacy image format' });
}
v2.6.0

Lazy loading advised for the LCP image

lib/image-optimisation-checker.js + audit-tools.html

Every image was flagged, including the one that should load eagerly.

Before
if (loading !== 'lazy') {
  issues.push({ type:'warning', label:'Image not lazy-loaded' });
}
After
const isLcpCandidate = (index === 0) ||
      (img.getAttribute('fetchpriority') || '').toLowerCase() === 'high';

if (loading !== 'lazy' && !isLcpCandidate) {
  issues.push({ type:'warning', label:'Image not lazy-loaded' });
}
v2.6.0

Long sentences undercounted

app.js + audit-tools.html

The count was read from the array after it had been truncated for display.

Before
const longSentences = sentences.filter(s => wordCount(s) > 25).slice(0, 8);
return { longSentences, count: longSentences.length };
After
const allLong = sentences.filter(s => wordCount(s) > 25);
const longSentences = allLong.slice(0, 8);
return { longSentences, longSentenceCount: allLong.length };
v2.6.0

Blocked platforms reported as broken profiles

audit-tools.html

Any non-200 became a red error, so a platform refusing bots looked like a dead profile.

Before
unverified.forEach(p => issues.push({
  type: 'error',
  label: p.label + ' profile not reachable',
  fix: 'Open the URL in a browser to confirm it works. If correct, this
        may be a platform anti-bot block - manually verify it loads.',
}));
After
const SAP_BOT_HOSTILE   = /linkedin|instagram|trustpilot|threads|tiktok|twitter|x\.com/i;
const SAP_BLOCKED_CODES = [401, 403, 405, 429, 451, 999];

unverified.forEach(p => {
  const st = parseInt(p.status, 10) || 0;
  const blocked = SAP_BLOCKED_CODES.indexOf(st) !== -1 ||
                  SAP_BOT_HOSTILE.test((p.label || '') + ' ' + (p.given || ''));
  issues.push(blocked
    ? { type: 'info',  label: p.label + ' could not be verified automatically' }
    : { type: 'error', label: p.label + ' profile not reachable' });
});
v2.6.0

Threads and Google Business Profile not detected

app.js

The domain lists predated Threads moving to threads.com and Google's short-link share formats.

Before
{ key: 'threads',         label: 'Threads',
  domains: ['threads.net'] },
{ key: 'google_business', label: 'Google Business Profile',
  domains: ['google.com', 'g.page', 'maps.google.com'] },
After
{ key: 'threads',         label: 'Threads',
  domains: ['threads.net', 'threads.com'] },
{ key: 'google_business', label: 'Google Business Profile',
  domains: ['google.com', 'g.page', 'maps.google.com',
            'maps.app.goo.gl', 'goo.gl/maps', 'share.google',
            'business.site'] },
v2.6.0

“You're not on any major social platforms”

audit-tools.html

A fixed rewrite replaced an accurate count with a claim that was false for anyone with some profiles but not all.

Before
{ test: /missing presence|missing.*platforms/i,
  label: "You're not on any major social platforms",
  fix: "Claim your handle on the big platforms
        (Facebook, Instagram, LinkedIn, Google Business)." },
After
// Entry removed. The original label - "Missing presence on N major
// platforms" - is already plain English and carries the real count.
v2.6.0

Tool directory ran off the screen

seo-audit-platform.html

Inline styles beat the stylesheet, so no media query could reach them. The chip rows could not shrink below 306px, which set the minimum width of each column.

Before
<div style="display:grid;grid-template-columns:1fr 1fr;gap:0 20px">
  <div style="display:flex;flex-direction:column">
    <div style="display:flex;gap:6px;flex-wrap:nowrap">
After
<div style="display:grid;grid-template-columns:repeat(auto-fit,minmax(240px,1fr));gap:0 20px">
  <div style="display:flex;flex-direction:column;min-width:0">
    <div style="display:flex;gap:6px;flex-wrap:wrap">
v1.9.0

The identity check validated almost anything

app.js

A character class, not a literal. It matched any single letter from i, d, e, n, t or y, so a file reading “hello there” passed.

Before
/^[identity]/m
After
/^\s*\[identity\]\s*$/mi
v2.0.0

Questions and answers capped at five

app.js

A hard-coded limit in the FAQ extractor overrode whatever count was selected.

Before
if (faqPairs.length >= 5) break;
After
if (faqPairs.length >= ITEM_CAP) break;
v2.0.0

ItemList emitted on every page

app.js

Where no list existed, a single item was fabricated from the page title.

Before
if (!items.length) {
  items = [{ '@type': 'ListItem', position: 1, name: pageTitle }];
}
graph.push({ '@type': 'ItemList', itemListElement: items });
After
if (items.length >= 2) {
  graph.push({ '@type': 'ItemList', itemListElement: items });
}

Found something reporting incorrectly? Tell us — audit accuracy fixes go to the front of the queue.