v0.0.175
Released Nov 11, 2026
Release Notes
Reports now say what happened to the local store and to cloud rendering: meta.storeIntegrity tells you when page bodies were evicted since an audit ran (JSON schema 1.10), and a render block records the render mode, the pages rendered and fetched plain, and why rendering fell back (schema 1.11), with a line on the console. squirrel audit and the MCP server share one run lifecycle, so the terminal status is sent once and a second Ctrl-C still lets it land, and a Ctrl-C'd crawl is recorded as stopped and resumes. links/broken-links counts the targets that answered a refusal or a throttle, ax/ai-crawlers warns when Googlebot is blocked for the audited URL, core/canonical flags a canonical that points at the home page, a11y/meta-refresh reads the Refresh response header, ax/content-signals tells absent, declared, invalid and not checked apart, and the cloud fetcher's waiter has one state instead of three booleans.
Added
ax/content-signalstells absent, declared, invalid and not checked apart (details.state). A robots.txt that was refused, failed or cut at the 512 KiB read cap is reported as not checked instead of as no Content-Signal declared; a plain 404 still counts as absent.a11y/meta-refreshalso reports aRefreshresponse header: a redirect names the target and delay, a header with no URL is a reload, and a page with both the header and a meta refresh gets one finding naming both. The rule now runs per page, and its solution recommends a 301 or 308 redirect.core/canonicalflags a page whose canonical points at the home page. A template that hard-codes the canonical to the site root tells search engines that every page it renders duplicates the home page, and the pages drop out of the index; the URL is well formed, so the rule passed it. A newcanonical-to-homepagecheck fails when a page that is not the home page declares the site root as its canonical (the tag first, then theLinkheader), and warns for a locale home page such as/en/. The root with a trailing slash,/index.htmlor thewwwor bare host is one root, a root with a query string is not, a page that redirects to the home page is not flagged, and every other page gets the same result as before. (#1685)links/broken-linkscounts the targets that answered a refusal or a throttle. Everybroken-linksrow now carriesrefusedTargets(a 401, a 403 or a bot-wall challenge) andrateLimitedTargets(a 429) in its coverage details, next tolinkTargets,probedTargetsandunfetchedTargets. Those targets are not broken and not unfetched, and nothing is known of the page behind them, solinkTargetslessunfetchedTargetsover-counts what was observed; subtract these two as well. No check message, item or status changes. (#1679)- A render that fell back to plain HTTP is visible in the report. When cloud rendering stops partway (out of credits, the render service failing, a signed-out or unreachable cloud), the audit now prints one line with the reason and how many pages were rendered, and the JSON report gets a
renderblock (requested,mode,pagesRendered,pagesFetchedPlainand afallbackReason) with a matching<render>element in the LLM report, so an agent can tell rendered pages from plain-HTTP ones. (#1664) squirrel pagesandsquirrel entitiessay when page bodies were evicted, andreportputs the count in its JSON. Content-store eviction is global by size cap, so an audit's page bodies can be gone later, and onlyreport,--resumeand re-audits said so.pagesandentitiesnow print the same notice on stderr (their rows are built from stored page data, so the output is complete), and with--format jsonall three carrystoreIntegrity(status: "evicted",evicted,stored):meta.storeIntegrityin thereportJSON, a top-level field inpagesandentities. It is absent when every body is still there. All readers share one census. (#1667)
Changed
ax/ai-crawlerswarns when Googlebot is blocked for the audited URL. Thesearch-engine-accesscheck was info only. A robots.txt that blocks Googlebot from the site root or for the entry URL usually means the site is not indexed, often by a stagingDisallow: /left in, so that check now warns and counts toward the score. AnAllowoverride, an allowed Googlebot and a block of any other engine (Bingbot,Applebot) stay info, anddetails.botsstill lists every bot. (#1680)
Fixed
- A second Ctrl-C no longer leaves the run waiting for the server to reap it.
squirrel audithandled the first interrupt withprocess.once, so a second Ctrl-C while the "cancelled" status was being sent killed the process under it, and the run sat pending until the server's reaper closed it as failed. The handler now stays installed: a later signal waits for the status already in flight, for at most 3 seconds, and then exits. The CLI and the MCP server also share one run lifecycle now, so the terminal status is sent at most once on every exit path and "running" can never be sent after it. (#1663) - A Ctrl-C'd crawl is recorded as
stopped, not leftpaused. The crawler's ownstop()writespausedfirst, and the interrupt handler only marked crawls that were stillrunning, so the stored status said paused where the audit was stopped. The handler now writes through the same guarded status write as every other change, which allowsstoppedfromrunningorpausedand never touches a crawl that already finished.--resumecontinues it as before. (#1672) - A report's "failed or blocked" and
--max-durationpartial status each have one definition.isAuditFailedandisScorelessin@squirrelscan/core-contractsreplace the check that was written out in about fifteen places, and one function,withMaxDurationCut, now makes a time-limited auditpartialfor both the live run andsquirrel reportrebuilding it from the store, so the two cannot disagree. Reports read as before. (#1662)
Update to this version
squirrelscan will auto-update, or run this command to update now: