What's new
Everything we've shipped, newest first. Subscribe to the changelog feed at /changelog/rss.xml — or just check back; we ship roughly weekly.
-
PDF scanning phase 2: rendered-page analysis
Opt-in PDF scanning now looks at the rendered page, not just the structure tree — deterministic rendered-text contrast (1.4.3, no model), vision-judged image-of-text detection (1.4.5 / 1.1.1), and a best-effort reading-order signal (1.3.2). Suspect-gated, capped at 10 pages per document, rasterized in memory and never persisted.
Phase 1 grades what a PDF’s structure tree declares. But some defects only show up once the page is drawn — text you can’t read because the contrast is too low, a “document” that’s really a scanned image, a tag order that doesn’t match the visual layout. Phase 2 rasterizes the suspect pages in-memory and derives exactly those signals. It’s opt-in (runs when a vision LLM is configured, or with the explicit
--pdf-visionflag), suspect-gated, and capped at 10 pages per document; the page rasters live in memory only and are never written to disk — the same rule as page screenshots.The design is a deliberate split by mechanism — not everything goes to a model:
- Rendered-text contrast (1.4.3) — deterministic, no model. We sample glyph-box pixels on the rasterized page and apply the WCAG ratio (4.5:1 normal, 3:1 large text). Contrast is the check a model is worst at — it hallucinates ratios — so we measure pixels instead: CI-deterministic coverage with zero model cost.
- Image-of-text detection (1.4.5 / 1.1.1) — vision judge. For the image-only pages phase 1 already flagged, the shared vision judge answers one question: “is this an image of text?” A decisive answer upgrades the phase-1 suspect to a real Does Not Support (text found) or Supports (purely pictorial).
- Reading-order plausibility (1.3.2) — best-effort signal. We compare the tag/content-stream order — what assistive tech announces — against the visual top-to-bottom, left-to-right order. A major divergence is flagged as a partial for human review, never asserted as proof.
Phase 2 folds into the same
Findingcontract, review queue, and VPAT as everything else; a decisive phase-2 verdict replaces the matching phase-1 suspect for that criterion.Dependency & degradation. Phase 2 adds one dependency —
@napi-rs/canvas(MIT), a rasterizer, not an accessibility engine (it draws pixels; every contrast/image-of-text/reading-order verdict is our own logic). If the native binding can’t load, phase 2 silently no-ops and the phase-1 structural verdicts stand. There is still no third-party accessibility engine anywhere in the pipeline.Honest limits that remain. Alt-text usefulness is still not graded — the model decides whether an image-only page is an image of text, but the privacy contract keeps
/Altstrings out of the model, so “does this alt accurately describe the figure?” stays a phase-1 existence check. Text over photos or gradients is reported indeterminate (a partial), never guessed. Reading order is a signal, not a proof. And where no renderer is available, you get phase-1 structural verdicts only. -
No overlay, ever — the VPAT now reflects only the scan
We've sharpened what Wholisphere is and isn't. There is no runtime remediation and no client-side injection — and there never will be. The VPAT reflects only what the scan found, with open issues disclosed rather than filled in. Source-code remediation is in development and described as forthcoming until it ships.
We’re making our positioning match the product exactly, because a compliance tool that overstates itself is worse than useless.
- No overlay, no client-side injection — ever. Wholisphere does not “remediate at runtime,” does not inject alt text or ARIA into your visitors’ browsers, and does not fix a site from a widget. That approach is fragile, breaks native assistive tech, and has drawn 200+ ADA lawsuits. We reject it on principle.
- The VPAT reflects only what the scan found. Nothing is derived, measured from an agent, or inferred. We no longer auto-fill a Section 508 §1194.5 “equivalent facilitation” block, and we do not position the Wholisphere agent as equivalent facilitation for your site. Every row traces to the scan’s evidence. An issue we can’t verify a fix for stays openly disclosed — never faked.
- Source-code remediation is in development. The real fix belongs at the source: a pull request against your repository, or a change made through your CMS, which you review and which we confirm by re-scanning. This is forthcoming — we’ll describe it as in development until it actually ships.
- The assistive agent is unchanged. The browser extension and embeddable agent that adapt a page for the individual end user — read-aloud, larger text, high-contrast, live captions, focus mode, voice control — are exactly as before. That’s a tool for the reader who turns it on; it was never site remediation, and we won’t describe it as if it were.
If you can’t fix an issue at the source yet, the honest answer goes in the report. We would rather tell you the truth than hide an unfixed issue behind a widget.
-
PDF document scanning — WCAG findings for the documents chapter
Opt in with --include-pdfs and the PDFs your site links are parsed and graded against 13 structural checks citing W3C PDF techniques. Document findings flow through the same pipeline, the same review queue, and the same VPAT as your pages — no second tool, no second report format.
Section 508 incorporates WCAG for electronic documents, and EN 301 549 gives non-web documents their own chapter. If your site links PDFs — forms, notices, reports — your conformance story has to cover them. Now the scanner does:
- 13 structural checks, each citing the W3C PDF technique it enforces: untagged documents (the catastrophic case), encryption that locks out assistive technology, missing document language (
/Lang), missing or hidden titles, Figure tags without alt text, image-only pages (the scanned-document signature), broken heading hierarchy, tables without header cells, form fields without labels or names, links with no text or bare-URL text, and missing bookmarks on long documents. - Same pipeline end to end. A PDF finding is a standard finding: confidence-based review routing, per-criterion VPAT aggregation. Clean documents contribute positive VPAT rows, same as clean pages.
- Opt-in and bounded. Nothing changes unless you pass
--include-pdfs. Parsing is fully in-memory with hard caps (25 MB per document, 500 pages), zero model calls, and evidence that stays structural — document text is read transiently to compute booleans and discarded. - In-house judgments, parser-only dependency. pdfjs-dist (Mozilla, Apache-2.0) extracts structure; every conformance verdict is our own evaluator logic. No third-party accessibility engine.
Honest limits (phase 1): no OCR — a scanned page is flagged as a suspect, not read; tag presence is checked, not reading-order correctness; contrast needs rendering and is deferred to phase 2 (rendered-page vision is on the roadmap). Password-protected documents are reported as Not Evaluated, never silently skipped.
- 13 structural checks, each citing the W3C PDF technique it enforces: untagged documents (the catastrophic case), encryption that locks out assistive technology, missing document language (
-
All 55 WCAG 2.2 A + AA criteria — automated hard-fail detection, verified
Every Level A and AA success criterion now has automated, deterministic-or-stability-verified hard-fail detection — 37 from static analysis, the rest from deterministic browser probes or stability-verified vision/text judges — pinned by three CI regression suites against a 196-page per-criterion fixture testbed. The 16 criteria that still route to human review are all Level AAA.
The number that matters: 55 of 55. Every WCAG 2.2 Level A + AA success criterion now has an automated path to a hard failure verdict, and every path is verified — not predicted:
- Static (37 criteria) — hard-fail from markup analysis alone, no browser needed.
- Browser (deterministic, no LLM) — Playwright-driven interaction probes (focus, sentinel typing, hover, zoom, shift-tab — with a record-not-obey guard so probes never trigger real navigation), temporal capture with measured flash frequency and reduced-motion emulation, a behavioral motion probe, and a cross-check against the browser’s native accessibility tree (never a third-party engine).
- Vision + text judges — full-page screenshot evidence and suspect-gated text judgments, each counted only after live model runs produced the verdict consistently.
2.3.1 Three Flashes, proven safely. The above-threshold flash fixture renders only for the automated harness (
navigator.webdriver-gated); human visitors always get a static warning card. The machine gets a genuine, measured violation — no human is ever exposed to hazardous flashing.Verification is the feature. Three standing suites gate CI against a 196-page fixture testbed with deliberate pass and fail examples for every criterion: static evaluators on every push, the full Chromium pipeline in the e2e job, and a live-model vision suite. Across the verification corpus — 23,862 findings, including the testbed’s deliberately clean pages — the engine produced zero false positives. (That’s our corpus, honestly measured; it is not an unconditional guarantee.)
100% of all 86 criteria are evaluated with evidence. The 16 review-only criteria are all Level AAA; they receive confidence-scored verdicts routed to human review, disclosed per-criterion in the VPAT. And all of it is 100% in-house detection — no axe-core, no pa11y, no Lighthouse; the only third-party code involved is parsers (happy-dom, Playwright).
-
Multi-step flow crawler — decisive verdicts for checkout-style flows
Opt in with --flow and the browser runner walks a checkout, signup, or application flow the way a customer does — and answers what no single-page scan can — whether the flow re-asks for information the user already typed (SC 3.3.7) and whether a review step exists before the final commit (SC 3.3.4). With a hard-stop safety contract.
Whether a repeated field is auto-populated, and whether a review step exists somewhere in the flow, are properties of the process, not of any one page. Single-page analysis can only ever say “Partially Supports, please attest.” The flow crawler walks the process once, deterministically, and observes both:
- SC 3.3.7 Redundant Entry (A) — a later-step field matching an earlier step’s purpose that arrives empty, with no “same as shipping”-style affordance, is a decisive Does Not Support. Every repeat pre-populated or covered → Supports.
- SC 3.3.4 Error Prevention: Legal, Financial, Data (AA) — reaching a final-commit control with no review/summary step anywhere in the flow → Does Not Support. Review step present → Supports.
The safety contract comes first. Ordinary page probes never click, submit, or press Enter — that still holds. The flow crawler is a separate, explicitly opt-in feature (
--flow <startUrl>) with enforced guardrails: same-origin only, a hard cap of 6 steps, deterministic sentinel data only (pre-filled fields are never overwritten, file inputs never touched, real user data never read), exactly one continue/next-style affordance per step, a never-click list for destructive controls — and a hard stop before any place-order / pay / purchase / confirm-order control. The crawler records that the commit boundary was reached and stops; our e2e suite asserts the commit button is never activated. Point it at staging or a test-mode checkout — that’s exactly why it’s opt-in.Flow findings join the same scan as your page findings, through the same pipeline, into the same VPAT.
-
Criterion selection — scan only what your site can fail
New --criteria, --skip-criteria, and --levels flags on both scan runners let you deselect WCAG criteria your site cannot fail. Deselected criteria switch off the browser captures they need — so a trimmed scan is genuinely faster and cheaper, not just a filtered report.
A marketing site with zero
<video>elements still paid for nine media evaluators and a per-element media audit on every page. A team certifying to AA paid for 31 AAA evaluators it would never report on. No more:--criteria <list>— scan ONLY these criteria. Accepts ids (1.2.2), guideline prefixes (1.2), or principle prefixes (2).--skip-criteria <list>— scan everything EXCEPT these (mutually exclusive with--criteria).--levels A,AA,AAA— keep only criteria at these conformance levels.--levels A,AAis the AA certification run: 55 criteria instead of 86.
The default — no flags — still scans all 86 WCAG 2.2 criteria, and unknown ids fail fast with the full valid list rather than silently scanning less than you asked for.
The savings are real, not cosmetic. In the browser runner, deselecting a criterion also switches off the capture work it needed: skip the media criteria and the per-media-element audit never runs; deselect the temporal criteria and no frame strips are captured; deselect every screenshot-consuming criterion (with no vision key set) and the screenshot itself is skipped. A capture that never happens saves seconds per page — and vision-LLM token cost when the vision family is deselected.
-
Vision-LLM scanner — four WCAG criteria rule-based tools cannot evaluate
The Wholisphere scanner is now multimodal. Four WCAG criteria are evaluated against the rendered page screenshot, not just the DOM. Plus a fully wired GitHub Action with auto-updating PR comments and the public sample VPAT showcasing every new feature.
The scanner-runner was a real product before this; now it ships verdicts a rule-based tool fundamentally cannot.
Vision-LLM evaluators (four, not one):
- SC 1.1.1 — Non-text Content. When alt text looks suspicious (filename, “image”, placeholder string), the agent verifies it against the rendered image. False alarms get cleared; genuinely-bad alts get escalated to Does Not Support — not a heuristic suspicion.
- SC 1.3.3 — Sensory Characteristics. “Click the green button”, “see the example to the right”, “press the round one” — the model decides whether the referenced control is also labeled (acceptable) or genuinely sensory-only (failure).
- SC 1.4.5 — Images of Text. The proof-of-moat criterion. There is no DOM signal that distinguishes a “Welcome to Acme” hero banner rendered as a JPG (a 1.4.5 failure) from a chart with axis labels (acceptable). The multimodal model is the only honest answer. axe / pa11y / Lighthouse cannot evaluate this. We can.
- SC 2.4.4 — Link Purpose (In Context). “Read more” links get verified against surrounding visible context — does the card title above disambiguate? Or is it the third “Read more” in a list with no nearby disambiguating text?
Plus four new static evaluators: SC 2.4.1 Bypass Blocks, SC 2.4.2 Page Titled, SC 3.1.1 Language of Page, SC 4.1.3 Status Messages. Per-criterion coverage now stands at eleven distinct WCAG criteria with real evaluator code, not just hand-curated VPAT samples.
GitHub Action upgrades.
- The
wholisphere-accessibility-scanaction now exposesgemini-api-keyso customers running it in CI can activate the multimodal upgrade without changing anything else. - On
pull_requestevents the action posts an auto-updating PR comment with the scan summary plus a diff vs the previous completed scan — regressions, improvements, unchanged, newly-evaluated. Marker-based update keeps the thread clean across the PR’s lifetime.
The public sample VPAT at
/v1/public/sample-vpatnow showcases every new feature — vision-LLM-flavored remarks on 1.1.1 / 1.3.3 / 1.4.5 / 2.4.4, a Human Attestations table with both directions of override (downgrade + upgrade), and an Equivalent-Facilitation block whose telemetry covers thirteen criteria. -
91% of WCAG 2.2 AA — automated
The Wholisphere scanner now ships 55 evaluators covering 50 of 55 procurement-relevant WCAG 2.2 AA criteria. The residual 9% is the WCAG-acknowledged judgment-call set — those flow through a one-click reviewer attestation surface, fully disclosed in every rendered VPAT.
A scanner that ships verdicts on more than half the WCAG matrix is a credible compliance tool. A scanner that ships verdicts on 9 out of 10 AA criteria — and is honest about the residual — is the difference between “useful” and “this can replace your manual VPAT engagement.”
Three architectural primitives carry the engine:
- Per-page Capability — 46 evaluators. 8 use vision-LLM (criteria a rule-based scanner cannot evaluate: 1.1.1 alt-text accuracy, 1.3.3 sensory-only instructions, 1.4.1 color-only meaning, 1.4.5 images-of-text, 1.4.11 non-text contrast, 2.4.4 link-purpose context, 2.4.6 heading descriptiveness, 3.1.2 language-of-parts).
- PageProbe — captures bounded snapshots from a Playwright-rendered page (tab order, reflow at 320px, text-spacing override, form validation). 5 evaluators consume it: 2.1.2 No Keyboard Trap, 2.4.3 Focus Order, 1.4.10 Reflow, 1.4.12 Text Spacing, 3.3.1 Error Identification.
- CrossPageCapability — collects a small fingerprint per page, then runs a single aggregate after every page completes. 4 evaluators on this surface: 3.2.3 Consistent Navigation, 3.2.4 Consistent Identification, 3.2.6 Consistent Help, 3.3.7 Redundant Entry.
Privacy-first by construction. Every probe and every fingerprint stores only structural signals — CSS selectors, counts, booleans, autocomplete tokens, location buckets. No raw DOM, no raw text content, no attribute values that could leak PII into evidence storage. Every snapshot is bounded with hard caps. Every probe call is wrapped in
Promise.racewith a per-probe timeout. AbortSignal honored at every step. Mutating probes restore page state infinally.Where the residual 9% goes. Five SCs are irreducibly human-judgment — 1.4.8 Visual Presentation (subjective formatting requirements), 2.3.1 Three Flashes (real-time video frame analysis at scan time), 3.3.4 Error Prevention (knowing whether a transaction is legally binding requires domain context), 3.3.8 Accessible Authentication (judging whether a function is a “cognitive function test” is subjective), 1.4.6 Contrast Enhanced (AAA-leaning). Our scan flags these for the reviewer queue (confidence < 0.6) and our existing Override workflow routes them to a named attester — whose attestation is rendered transparently in the VPAT’s Human Attestations subsection alongside the original aggregator verdict and the reviewer’s verbatim reason.
The procurement story is now: “Your VPAT is generated by an engine that automates 91% of AA. The remaining 9% is one-line human attestations, audit-logged and disclosed in the document. We’re not pretending to do 100%; we’re being honest about what’s automatable and what isn’t — and we’re the only scanner reaching this ceiling.”
-
Scheduled scans — recurring compliance, not one-shot
New `/v1/orgs/:orgId/scheduled-scans` resource configures the scanner to fire at a customer-defined cadence — Mondays at 06:00 UTC, the first of every month, every push to staging. The cron worker that fires due schedules ships in the next release; configuration persists today.
The scanner has been a one-shot CLI / GitHub Action surface since launch. That works for deployments (run on every PR) but doesn’t cover the between-deploy drift enterprise compliance teams worry about — a third-party widget changes its CSS, an A/B-test variant adds an inaccessible flow, a content team uploads a hero image without alt text. Ad-hoc audits miss those.
Scheduled scans turn the scanner into a recurring service:
- Configure via
POST /v1/orgs/:orgId/scheduled-scans(or the dashboard once Phase 2 ships): label, 5-field cron expression (0 6 * * 1= Mondays at 06:00), IANA timezone, target URLs or sitemap, optionalfailOnRegressionflag. - Same scanner pipeline as the CI integration: 50 evaluators including 8 vision-LLM, 5 PageProbe-driven, 4 cross-page aggregators. 91% of WCAG 2.2 AA automated, residual 9% routed to the existing reviewer-attestation flow.
- Same regression detection: when conformance drops vs the previous successful run for the same product, the run is marked failed and the existing
scan.failedwebhook fires — wire it to your Slack channel, your incident-response queue, your accessibility-team PagerDuty.
Security-by-construction in Phase 1’s schema: no API keys, no Playwright
storageState, no auth blobs in thescheduled_scanstable. Sensitive material flows through a separate secrets surface keyed by (org, schedule_id) at execution time. The schema-as-contract guarantees we cannot leak the wrong material out of the wrong endpoint.What’s in this release (Phase 1):
- Schema + repo + CRUD routes (POST / GET list / GET item / PATCH / DELETE)
- 5-field cron validator (Quartz-style 6/7-field rejected — keeps the surface auditable)
- Lenient IANA timezone validator
- Bounded inputs: max 500 URLs per schedule, label 1-200 chars
- Owner/admin role required for mutations, viewer for reads
- OpenAPI 3.1 registration so codegen customers (
npx openapi-typescript-codegen) get typed clients immediately - 36 tests covering the full validation surface
What’s coming (Phase 2):
- Cloudflare Cron Trigger that wakes every minute, queries due schedules globally, fires the scanner-runner workflow per row
- New webhook events:
scheduled-scan.run.started/succeeded/failed - Dashboard UI: “Scheduled scans” tab on the Compliance page, per-schedule history, “Run now” button
- Regression-email integration so a regressed run triggers an audit-logged Resend email to the configured accessibility owner
Configure schedules today via API; they will start firing automatically once Phase 2 ships next week.
- Configure via
-
Demo glue — read-aloud, voice control, playground
Read-page TTS controller + playground demo page + extension popup site preferences. The pieces from the last release are now usable end-to-end.
A run of “make it usable, not just buildable”:
- Read-aloud TTS controller walks a page outline section-by-section with pause / next-section / jump-to / stop. Highlights the active line via
setActiveLine. - Voice command surface unified: every voice-driven DOM action goes through the same sandboxed
executeVoiceCommand(selector sanitation, off-origin nav block, 10 KB input cap, confidence threshold). /playgroundon the dashboard — live demo article + buttons for read-aloud, focus mode, captions. The demo URL we’ll point everyone at.- Extension popup “This site” panel — toggle widget per-host, auto-enable focus / captions, snooze 24h. Backed by
chrome.storage.syncso it travels across devices.
- Read-aloud TTS controller walks a page outline section-by-section with pause / next-section / jump-to / stop. Highlights the active line via
-
Sales velocity, ops, and EU residency
Admin ops dashboard, customer onboarding wizard, EU data residency v1, widget Arabic + RTL, weekly customer digest emails.
A grab-bag bucket aimed at moving paid-customer time-to-value:
- Admin operations dashboard at
/admin— list orgs, suspend / reactivate, audit-logged. Founder/oncall view. - Customer onboarding wizard on the dashboard Overview — 3-step checklist that lights up as you create your first key, invite a teammate, make your first API call.
- EU data residency v1 —
regionMiddlewarereturns 421 + correct-host headers when an org pinned to one region hits another. Owner-only region pinning. - Widget Arabic locale + RTL —
'ar'strings +dir="rtl"auto-flips the panel layout. - Weekly customer digest email — opt-in per user; usage delta + top capabilities + open incidents.
- Admin operations dashboard at
-
North Star — agent replaces assistive tech
Five capabilities that move from "AI helper" to actual AT replacement — read-page outline, focus mode, live captions, voice DOM control, per-site extension preferences.
This is the one we’ve been building toward.
POST /v1/read-pagereturns a structured page outline (summary, sections with headings + paragraphs, links with intent, landmarks). Quota-counted, PII-scrubbed.- Focus mode — pure-DOM cognitive accessibility: clutter removal, line spacing, optional Atkinson Hyperlegible font, line-by-line highlight. Free for everyone, no LLM call.
- Live captions surface — mirrors any
<track kind="captions">cues onto a high-contrast overlay. MutationObserver picks up dynamic media. STT extension hook ready for v2. - Voice DOM control —
executeVoiceCommandruns the voice-action response with hard guardrails (sanitized selectors, off-origin nav block, 10 KB cap, confidence threshold). - Per-site preferences in the extension —
chrome.storage.syncper host, snooze, cross-device.
-
Tier 2 polish — SLO board, scoped keys, deprecation policy
Internal SLO dashboard, API key IP allowlists + scopes, webhook event-type subscriptions, API versioning + Deprecation/Sunset headers, end-user activity feed.
Five professional-polish wins:
- Internal SLO dashboard — sampled
request_metricstable +MetricsRepo.sloByRoute()returns p50/p95/p99 + error rate per route. Drives oncall + SLA reporting. - API key IP allowlists + scope refinement — per-key CIDR enforcement, canonical scope list (
agent:rw,describe,vpat, …). Returns 403ip_not_allowed/ 403scope_insufficient. - Webhook subscriptions by event type — typed event registry (10 kinds + wildcard); dashboard checkbox UI; drain already filters by org + kind.
- API versioning + deprecation headers —
x-wholisphere-api-version: v1on every response. RFC 8594Deprecation: true+Sunsetheaders ready to deploy. Public docs page. - End-user activity feed in the widget — opt-in tab in the panel, in-memory ring buffer, four-locale i18n.
- Internal SLO dashboard — sampled
-
Enterprise readiness — multi-user orgs, SSO, quotas, status page
Multi-user orgs + roles + invitations, Google Workspace OIDC SSO, per-org monthly quotas, public status page + uptime probes, audit log filters + CSV + retention.
Tier 1 (enterprise) blockers cleared:
- Multi-user orgs + roles + invitations —
editorrole joins owner/admin/viewer/api. Email-link invites, last-owner protection, dashboard Members page. - Google Workspace OIDC SSO — code-flow + tokeninfo verification, per-org allowed domain, auto-join with
auto_assign_role. “Sign in with Google” on the Login page. - Per-org monthly quotas —
usage_monthlytable +quotaMiddleware. Returns 429quota_exceededwithX-Quota-Limit/Remaining/Resetheaders. Dashboard usage panel. - Public status page + uptime probes — new
apps/statusAstro app at status.wholisphere.ai. - Audit log filters + CSV export + retention — kind/since/until filtering, RFC 4180 CSV, plan-aware nightly prune (free + pro 90d, platform 1yr, enterprise unlimited).
- Multi-user orgs + roles + invitations —