v0.0.180
Released Nov 13, 2026
Release Notes
squirrel ci gates a deploy on the findings it adds, --target-url audits exactly the URLs you name, an audit that ran out of credits says so in every report, and [[ignore]] reasons now show up in all of them. The bundled agent skills move to squirrelscan 2.0 and audit-website 3.0.
Added
- New rule
content/voice-consistency. Flags pages whose reading level is far from the rest of the site: the page written by a different hand, text pasted in from elsewhere, or bulk-generated copy. The norm is the median Flesch Reading Ease score of the site's own pages; a page at least 25 points away (harder or easier) is reported with its score and the site norm. Legal, privacy and terms pages are never judged (counted inexcludedLegalPages), and the rule skips a crawl under 10 pages, under 10 scorable pages, or with no clear norm. It is a normal local rule: no model, no account, no surcharge. The score is stored per page (page_features.reading_level, schema version 41) so the streaming and legacy paths give the same answer. (squirrelscan/repo#1384) squirrel ci <url> --website <id> [--fail-on-new error|warning|info]audits a deployed URL in the cloud as one of your websites (a real cloud run, in the website's audit history), waits for it, and exits2when the audit has findings new since the previous completed audit at or above the severity. It lists each new and resolved finding, how many older findings could not be compared, the report link and the credits used.0passes (the first audit of a website prints that there is no baseline),3blocked,5unreachable,1an operational error or a comparison that could not be evaluated. Needs an org API key withaudits:readandaudits:write. The public API docs gainGET /v1/websites/{websiteId}/audits/compareand itsAuditComparisonandCompareGateschemas, and the CI guide a "Fail the build on new findings" section (#1776)squirrel audit --target-url <url>(repeatable) audits exactly the URLs you name and nothing else (#1777): no sitemap, no discovery probes, no link following. Each URL goes through the normal scope, robots and length checks and--max-pagescaps the list. Rules that judge the whole site skip with the reasontargeted-runand report as not checked, never as a pass or a finding, so a targeted run cannot resolve a finding it did not check. A redirecting URL is recorded as a redirect and its destination is audited only when it is listed too. The report accounts for every URL given (requested, audited, and each one not audited with its reason):scanScope.targeted, andmeta.targetedin the JSON report (schema 1.16). The engine option istargetUrls. A targeted run is not auto-published (it would replace the site's latest audit with a few pages;--publishpublishes it, andpublish.reasonistargeted), checks no links, is cut at 1,000 URLs (the rest counted inoverLimit), and is refused a--resumeacross a different list. See Targeted audits.- Locked and not-checked rules can carry the reason
credits(#1792): the audit ran out of credits before the check ran. Every report groups and words it on its own ("ran out of credits: add credits, then re-run the audit"), never as a plan or an environment limit, and the Free account keeps its add-credits link. A reader from before this release treats the new value as no reason and keeps the generic wording. - A site page record can carry
canonical, the page a query-string variant counts with. The cloud merge records a crawl's variants (report.canonicalVariantPages), counts them with their canonical page incoverage.knownPages(auditedPagesstays as crawled), and returns the site's variant map so a server folds the merged report after scoring.generateReportFromStoragetakesexportCanonicalVariantsto list the variants without folding: the list is always sent when the page features could be read, an empty one included, andcanonicalVariantsComplete: falsemarks a list clipped at 1000. A complete list is the whole truth for the crawled pages (a page it no longer names stops being recorded as a variant); a clipped one only adds.coverage.carriedPagesOutsideCrawlcounts pages that carry a finding and are not in the run's crawl, only when the caller asks. A site with no variants reports exactly as before. #1350 #1031 - Reports explain an overall score that sits below its section scores (squirrelscan/repo#3915): when the engine recorded a site-wide adjustment in
healthScore.debug, the HTML, Markdown, text and LLM reports say "Overall 72: 95 before a 24% site-wide adjustment for issue density across the whole audit. Section scores use only their own checks." Shown only when the report has section scores and the adjustment is at least 1%. No score changes.scoreAdjustmentandscoreAdjustmentNoteForin@squirrelscan/reportgive the same numbers and wording to other surfaces. - A cloud report can say what became of the known pages that carry open findings (#1759):
coverage.seedVerificationcounts the pages re-verified (gone with their 404 or 410 status, redirected with source, destination and status, or still live) and the pages left carried, split into skipped by the page budget, no longer linked and unverified. The console, text, Markdown, HTML and llm formats state it and the JSON report carries it, whenever a report has it; a local CLI audit does not seed pages, so its report is unchanged.summarizeSeedVerificationis exported from@squirrelscan/core-contracts/seed-verificationfor the hosted merge, andrunSmartAuditsaccepts the run'sseedVerification(seeds and frontier) for a caller that seeds. What the merge resolves or carries is unchanged.
Fixed
core/h1tells hidden H1s apart from visible ones. A page with one shown H1 and two alternatives underhiddenor an inlinedisplay:noneno longer readsMultiple H1 tags found (3)like a page with three shown H1s: it readsMultiple H1 tags found (1 exposed, 3 in the HTML; ...)and still warns, since crawlers that do not render the page read every H1. The details keep the raw count asrawCountbesideexposedCountandhiddenCount. Two or more shown H1s warn on the exposed count, and an H1 a style may hide but squirrel cannot settle is reported onh1-unconfirmed. The messages say that an HTTP-only audit cannot resolve visibility set by external stylesheets or scripts. A page whose H1s are all shown reads as before. (#1636)crawl/robots-txtno longer says every crawler is blocked whenUser-agent: *hasDisallow: /but aGooglebotorBingbotgroup allows the site. That file now gets a new warning,robots-txt-disallow-wildcard, which names the crawlers still allowed.robots-txt-disallowkeeps its meaning: it fails only when every crawler is blocked. The two checks are never reported together. The rule now declares its checks (meta.checks).squirrel ciprints the report's own address (https://<org>.squirrel.report/..., read from the API) on itsReport:line and in--json, assquirrel auditdoes, instead of the id on the olderreports.squirrelscan.comhost; it falls back to that host, which redirects, only when the address cannot be read (#1730).- The rule cache and template fan-out now run together (#296). Before, a run with the cache did not fan out, which cost the cold rules phase about 7%. A cached page records its template cluster and the page whose template-shared verdicts it holds; when the first page of a cluster changes, the pages that inherited its verdict are checked again, so a fully replayed audit is byte-identical to a fresh one. Entries written by an earlier release replay only when fan-out is off (
SQUIRREL_TEMPLATE_FANOUT=0), and are otherwise checked again once. - A capped (published) report keeps coverage-gap skip rows instead of cutting them like a "does not apply" skip (#1775). Per rule, up to 5 skipped rows whose outcome is a coverage gap, a timeout or an error (
scan-truncated,unread-page,rule-timed-out, ...) are kept per page with theirpageUrl, the rest fold into one class per skip reason, and each check's tally gainsgaps(a count by reason), so a reader sees a gap even when no row survives.not-applicable,noindex,page-typeand free-text skips are never marked as a gap. [[ignore]]reasons and unmatched entries now reach every report (#1262): an entry with areasonthat waived something prints its own line (Ignore entry 1 (security/cookie-flags), Shopify sets these: 3 findings waived.) in the console, text, Markdown and HTML reports, and the HTML report now lists the entries that matched nothing, as the other formats already did (an unmatched entry keeps its reason). The docs say which rules rewrite their message from the remaining items and which keep their text with an(N ignored)suffix.
Changed
- Bundled agent skills updated: squirrelscan 2.0, audit-website 3.0 (served by both MCP servers).
Update to this version
squirrelscan will auto-update, or run this command to update now:
$