Product · audits
Automated Website Evidence Audit
Seven observable technical domains plus an evidence-linked remediation ledger. Interaction testing is not included.
Inputs and outputs
You supply
- One public URL to start from
- Optional list of additional public URLs on the same origin
- Optional list of paths to exclude
You receive
- Desktop and mobile render evidence at fixed viewports
- Machine-checkable accessibility findings mapped to WCAG 2.1 where applicable
- Technical SEO and metadata inventory for checks this definition implements
- Third-party request and storage inventory
- Asset integrity results for requested subresources
- Link and navigation resolution results with HTTP status per target
- Browser console and page error transcript
- Explicit not_covered list including the entire interactions domain
- Prioritized remediation ledger with severity and evidence references
When checkout opens, the entire purchase is a URL, a payment, and a download link. There is no intake form, no scoping call, and no questionnaire, because the scope is fixed and published on this page.
What this is
A run against a public website that records what a real browser engine actually
does with it — at a desktop viewport and a mobile viewport — and files the
result as evidence. Seven observable technical domains plus an evidence-linked
remediation ledger. Interaction testing is not included. Interaction probes
are published as not_covered and are not part of the offer. Every
finding names the artifact that produced it and a stable check_id. The report
ends in an ordered list of work rather than a score.
It is deliberately narrow. It reads a site and reports. It does not log in, it does not submit anything, it does not drive the keyboard, it does not change anything, and it does not guess. Checks the runner does not implement are listed as not covered rather than implied by silence.
What you get back
Two files. A static HTML report that opens in a browser with no network access required, and a JSON document with the same content in machine-readable form so it can be diffed, filed, or fed into your own tooling. Both are self-contained. Both carry the runner version, the browser engine version, and the audit definition version that produced them, so a report from six months ago can be explained.
What it will not do
The boundaries below are the product. They are published here rather than buried in terms, because the reason to trust an unattended process is knowing exactly how far it is allowed to go.
Seeing one before checkout opens
Checkout is not open, so there is nothing to buy on this page yet. There is a private demo: a code-gated route that runs the real pipeline and hands back the real package. It is not a signup and there is nothing to fill in unless you already hold the admin code, which is given out one conversation at a time.
Why there is no score
A single number sums unrelated evidence into something that cannot be acted on and can always be argued with. Instead the report ends in a ranked ledger: grouped by cause, ordered by severity and by how many routes each item affects, with every entry pointing back at the artifact that justifies it. The point of the run is to end in a decision about what to do next.
9 evidence domains · definition v1
Every check, every artifact, every limit
Expand a domain to read exactly what is examined, what is kept as proof, and what the domain does not cover.
Desktop and mobile rendering partial · 5 checks
Record what a real browser engine paints at fixed viewports, so layout claims are settled by an image and a measurement rather than by memory.
Checked
- rendering.full_page_capture (implemented · capture) — Full-page capture at desktop 1440x900 and mobile 390x844
- rendering.horizontal_overflow (implemented) — Horizontal overflow measured as scrollWidth against clientWidth
- rendering.text_below_minimum_size (implemented) — Minimum rendered text size per text node against a published floor
- rendering.elements_clipped_or_overlapping (not_covered) — Elements clipped or overlapping at either viewport
- rendering.layout_shift_late_assets (not_covered) — Layout shift caused by late-loading local assets
Evidence kept
- Screenshot per route per viewport
- Measured document and element geometry as JSON
Not covered
- One browser engine, stated by name and version in the report
- No visual judgement of taste, hierarchy, or brand fit
- No emulation of assistive hardware or real devices
Accessibility partial · 12 checks
Check the machine-checkable part of WCAG 2.1 AA and say plainly that the rest still needs a person.
Checked
- accessibility.missing_h1 (implemented) — Missing h1 on a captured route
- accessibility.multiple_h1 (implemented) — More than one h1 on a captured route
- accessibility.heading_level_skipped (implemented) — Heading order skips a level
- accessibility.link_without_accessible_name (implemented) — Link without an accessible name
- accessibility.image_missing_alt (implemented) — Image alternative text missing where required
- accessibility.contrast_below_aa (implemented) — Text contrast ratio below WCAG 2.1 AA against resolved colours
- accessibility.missing_document_language (implemented) — Document language attribute missing
- accessibility.missing_document_title (implemented) — Document title missing
- accessibility.landmark_and_skip_link (not_covered) — Landmark structure and skip-link target resolution
- accessibility.visible_focus_indicator (not_covered) — Visible focus indicator on every keyboard-reachable control
- accessibility.reduced_motion (not_covered) — Reduced-motion handling for animated elements
- accessibility.button_and_control_names (not_covered) — Accessible names for buttons and form controls beyond links
Evidence kept
- Per-finding CSS selector and DOM snippet
- Computed contrast ratio with both resolved colour values
- Mapping to the specific WCAG 2.1 success criterion where applicable
Not covered
- Automated checks cover a minority of WCAG 2.1 criteria
- No screen-reader output is simulated or scored
- Cognitive, language, and content-comprehension criteria are out of scope
- A clean automated result is not an accessibility conformance claim
- Translucent colours are skipped rather than approximated
Technical SEO and metadata partial · 9 checks
Inventory the machine-readable claims a page makes about itself and flag the ones that contradict each other.
Checked
- seo.duplicate_title (implemented) — Document title duplicated across captured routes
- seo.missing_meta_description (implemented) — Meta description tag missing on a captured route
- seo.robots_txt_without_sitemap (implemented) — robots.txt reachable without a Sitemap directive
- seo.canonical_url (not_covered) — Canonical URL presence and self-consistency
- seo.opengraph_twitter_cards (not_covered) — OpenGraph and Twitter card completeness
- seo.structured_data (not_covered) — Structured data presence and JSON parse validity
- seo.title_description_length (not_covered) — Title and meta description length scoring
- seo.sitemap_route_agreement (not_covered) — Sitemap contents fetched and compared to discovered routes
- seo.heading_outline_link_depth (not_covered) — Heading outline and internal link depth as an SEO inventory
Evidence kept
- Extracted title presence and uniqueness across routes
- Meta description presence per route
- robots.txt text as recorded in the scope record
Not covered
- No rank, traffic, difficulty, or competitor estimates
- No keyword recommendations
- Search engines are not queried; only the site is read
- Description body text is not persisted by the current DOM summary
Privacy and third-party surface partial · 8 checks
Record what the page contacts and what it stores, so the privacy policy can be compared against observed behaviour.
Checked
- privacy.third_party_origin (implemented) — Every outbound request grouped by third-party origin
- privacy.third_party_script (implemented) — Third-party executable script requests recorded
- privacy.cookie_set_on_load (implemented) — Cookies present after load, by name and attributes, never by value
- privacy.web_storage_written_on_load (implemented) — localStorage and sessionStorage key names present after load
- privacy.insecure_transport (implemented) — Transport security of every request
- privacy.blocked_by_audit_policy (implemented) — Requests the audit runner itself refused, with the reason
- privacy.tracker_classification (not_covered) — Classification of origins as analytics, tag-manager, or advertising
- privacy.consent_gated_behaviour (not_covered) — Behaviour after a consent interaction (consent UI is not exercised)
Evidence kept
- Request ledger with origin, resource type, and initiator
- Storage ledger with key names and attributes
Not covered
- Observed behaviour on the audited routes only, at run time
- No legal opinion on GDPR, CCPA, or any other regime
- Server-side data handling is invisible to this audit
- Cookie and storage values are never read
Asset integrity partial · 4 checks
Prove that every local asset the page references actually resolves, because a green build does not.
Checked
- asset-integrity.request_failed (implemented) — HTTP status recorded for every subresource the page requested
- asset-integrity.unexpected_content_type (implemented) — Response content-type compared against the requested resource type
- asset-integrity.unrequested_markup_references (not_covered) — Assets referenced in markup but never requested during the load
- asset-integrity.favicon_manifest_social (not_covered) — Favicon, manifest, and social-card image reachability as dedicated checks
Evidence kept
- Asset ledger with source reference, resolved URL, and status from the request ledger
Not covered
- Reachability and content type only; no image quality assessment
- Third-party asset availability is recorded, not guaranteed
- Requests with no recorded response are not asserted as failures
Link resolution partial · 6 checks
Follow every discovered link once and record what came back.
Checked
- links.navigation_failed (implemented) — Final HTTP status of every navigation recorded from the request ledger
- links.route_excluded_from_scope (implemented) — Routes excluded from scope recorded with their reason
- links.dom_links_not_requested (not_covered) — Links present in the DOM but not requested during the observed load
- links.fragment_targets (not_covered) — Fragment targets checked against the destination document
- links.redirect_hop_enumeration (not_covered) — Redirect chains enumerated hop-by-hop per link
- links.trailing_slash_variants (not_covered) — Links that resolve only with a trailing-slash variant
Evidence kept
- Link and navigation outcomes from the request ledger
- Scope record of excluded routes and reasons
Not covered
- One crawl depth boundary, stated in the report
- Authenticated, rate-limited, or bot-blocked targets are reported as such
- Link presence is not judged for editorial relevance
- Outbound links are never followed further as crawl seeds
Browser console and page errors partial · 6 checks
Capture the errors a visitor's browser already reports and nobody reads.
Checked
- console.page_error (implemented) — Uncaught JavaScript exceptions with message text
- console.error (implemented) — Console error entries during the observed load
- console.warning (implemented) — Console warning entries during the observed load
- console.failed_network_requests (not_covered) — Failed network requests ranked inside the console domain
- console.csp_violations (not_covered) — Content Security Policy violation reports as a separated class
- console.mixed_content_blocked (not_covered) — Mixed-content and blocked-resource events as a separated class
Evidence kept
- Timestamped console transcript per route and viewport
Not covered
- Errors raised during the observed load window only
- Errors behind authentication or user-specific state are not reached
- Console log, info, and debug entries are recorded but not ranked
- Stack frames beyond the reported message text are not persisted
Interaction probes not_covered · not sold · 6 checks
Exercise the parts of a page that only misbehave once touched.
Checked
- interactions.keyboard_traversal_order (not_covered) — Keyboard traversal order against visual order
- interactions.focus_visibility_sequence (not_covered) — Focus visibility at every stop in the tab sequence
- interactions.disclosure_menu_dialog_keyboard (not_covered) — Disclosure, menu, and dialog controls opened and closed by keyboard
- interactions.focus_return_after_close (not_covered) — Focus return after a dialog or menu closes
- interactions.hover_only_affordances (not_covered) — Hover-only affordances that have no keyboard equivalent
- interactions.layout_stability_during_transition (not_covered) — Layout stability during and after transitions
Evidence kept
- None in this definition version — no probe transcript is produced
Not covered
- Interaction probes are not implemented in this definition version
- The capture runner observes a page load and does not drive the keyboard
- No form submission, account creation, purchase, upload, or other state-changing request is ever performed
- Because no probe ran, an empty result for this domain is not evidence that interactions behave correctly
- Flows requiring credentials are never attempted
Prioritized remediation ledger covered · 5 checks
Turn findings into an ordered list of work, so the report ends in a decision rather than a feeling.
Checked
- remediation.severity_from_rubric (implemented · process) — Severity assigned from the published rubric in data/report_schema.yaml
- remediation.group_by_shared_cause (implemented · process) — Findings grouped by shared cause so one fix closes one ledger entry
- remediation.link_evidence (implemented · process) — Each ledger entry linked back to the findings and artifacts that produced it
- remediation.order_severity_then_breadth (implemented · process) — Ordering by severity, then by breadth of affected routes
- remediation.mark_human_review (implemented · process) — Items that a machine check cannot close are marked for human review
Evidence kept
- Ordered remediation table with severity, affected routes, and evidence IDs
Not covered
- Effort and cost estimates are not produced
- No code changes are written, proposed as patches, or applied
- Business priority is yours; the ledger orders by evidence only
Crawl budget · wea-v1-crawl-budget · definition v1
Published crawl budget
These are the numeric limits the runner enforces. The sold product uses the API path numbers. The operator CLI ships different defaults for local runs; that difference is intentional and stated here so it is not a surprise.
| Limit | Sold product (API) | Operator CLI default |
|---|---|---|
| Maximum in-scope routes | 25 | 50 |
| Maximum total HTTP requests | 2000 | 2000 |
| Maximum redirect hops | 5 | 5 |
| Minimum request interval (cooldown) | 0.5 seconds | 0.5 seconds |
| Wall-clock timeout | 900 seconds (API job / crawl ceiling) | 300 seconds |
| Crawl transfer-byte cap | 67108864 bytes (64 MiB aggregate declared responses) | 67108864 bytes |
| Persisted artifact-byte cap | 134217728 bytes (128 MiB) | 134217728 bytes |
| Per-request timeout | 45 seconds | 45 seconds |
| API job queue depth | 4 | n/a (CLI is single-run) |
| Ownership challenge rate limits | 5 per host / 20 global per 3600s window | n/a |
Viewports: desktop 1440×900 and mobile 390×844. robots.txt is respected by default. Buyers cannot raise these limits at purchase. Truncation and not-reached routes are recorded in the report.
Report schema · v1 · frozen
The output format, frozen at version 1
The machine-readable half of a report is a single JSON document. Its shape was published here before the code existed, then frozen (wea-v1-report-schema-freeze) so consumers can rely on required fields. The derivation engine is held to it by contract tests that derive a real report and check every required field below. Within version 1 only additive optional fields are allowed. A breaking change gets a schema_version bump and an entry in the launch log.
| Field | Type | Required | Description |
|---|---|---|---|
| schema_version | integer | yes | Version of this document schema. Increments on any breaking change. |
| report_id | string | yes | Opaque identifier for the run, unique per audit. |
| generated_at | string (RFC 3339) | yes | UTC timestamp at which the run completed. |
| target | object | yes | The audited origin, the entry URL, and the resolved route list. |
| runner | object | yes | Browser engine name and version, runner version, and audit definition version. |
| scope | object | yes | Crawl budget, viewports used, routes visited, and routes deliberately excluded. |
| domains | array of object | yes | One entry per evidence domain, each carrying its own findings and limits. |
| findings | array of object | yes | Flat list of every finding, referenced by the remediation ledger. |
| remediation | array of object | yes | Ordered remediation ledger. |
| artifacts | array of object | yes | Every screenshot, transcript, and ledger produced, with a content hash. |
| not_covered | array of string | yes | Explicit list of what this run did not examine, and why. |
| summary | object | no | Finding and remediation counts, and counts per severity. Derived from the lists below; carried so a reader does not have to recount them. |
| Field | Type | Required | Description |
|---|---|---|---|
| id | string | yes | Stable within a report; referenced by remediation entries. |
| domain | string | yes | One of the nine evidence domain identifiers. |
| severity | string | yes | One of the severity levels below, assigned by rubric. |
| title | string | yes | One line stating what was observed, not what it implies. |
| observed | string | yes | The measurement or event exactly as recorded. |
| expected | string | yes | The rule that was applied, and where that rule comes from. |
| routes | array of string | yes | Routes on which the finding was reproduced. |
| selector | string | no | CSS selector for the element, where the finding is element-scoped. |
| artifact_ids | array of string | yes | Artifacts proving the finding. A finding with none is not emitted. |
| verified_by | string | yes | machine — every published finding is machine-verified. |
| check_id | string | no | Namespaced identifier of the rule that produced this finding, as domain.check. Stable across reports; use it to track one defect over time. |
| status | string | no | One of the status values below. Defaults to reproduced. |
| viewports | array of string | no | Viewports the finding was reproduced at, where it is viewport-specific. |
| action | string | no | What to change, stated as an outcome. Repeated on the remediation entry that groups this finding. |
| needs_human_review | boolean | no | True when the finding records something a machine cannot judge — a third-party origin that may be intended, a cookie that may be necessary. |
| Field | Type | Required | Description |
|---|---|---|---|
| rank | integer | yes | Position in the ledger, ascending. |
| finding_ids | array of string | yes | Findings resolved by this item; grouped by shared cause. |
| severity | string | yes | Highest severity among the grouped findings. |
| affected_routes | integer | yes | Count of routes on which the grouped findings appear. |
| action | string | yes | What to change, stated as an outcome rather than as a patch. |
| needs_human_review | boolean | yes | True when the item cannot be closed by a machine check alone. |
| group_key | string | no | The shared cause that grouped these findings. Two symptoms with one key are one fix. |
| artifact_ids | array of string | no | Every artifact cited by the findings this entry groups. |
Severity rubric
Severity is assigned from these definitions. No finding is escalated because it sounds serious, and none is softened because it is awkward.
- blocking
- Observed behaviour prevents a documented task from completing at all for some visitors — a keyboard trap, an unreachable control, a 5xx on a route linked from the entry page.
- serious
- Observed behaviour degrades a documented task or violates a stated contract — a WCAG 2.1 AA failure, a broken internal link, an asset that does not resolve, an uncaught exception on load.
- moderate
- Observed behaviour is incorrect but has a working path around it — a redirect chain, a duplicated title, a missing canonical URL.
- minor
- Observed behaviour is inconsistent with the site's own conventions and carries no known user-facing impact.
- informational
- Recorded for the ledger with no defect asserted — a third-party origin inventory entry, a redirect that behaves as intended.
Finding status values
- reproduced
- Observed on every attempt within the run.
- intermittent
- Observed on some attempts; attempt count is recorded.
- not_reached
- In scope but unreachable — authentication, rate limit, or timeout.
- excluded
- Deliberately outside the run's stated scope.
Hard automation boundaries
What the runner is never allowed to do
These limits are enforced in the runner, not promised in prose. Anything on this list occurring during a run is a defect, and would be reported as one.
The runner never authenticates.
No credentials are accepted, stored, or used. Anything behind a login is out of scope and is reported as not reached, never as passing.
The runner never performs a state-changing request.
No form submissions, no purchases, no uploads, no account creation, no requests outside safe HTTP methods against the audited origin.
The runner never writes to your infrastructure.
No files, no DNS records, no repository commits, no configuration changes. The audit reads and reports; you decide what to change.
A finding without an artifact is not published.
Every entry in the report references the screenshot, header, console line, or DOM assertion it came from. If the artifact is missing, the finding is dropped rather than described.
Severity comes from a published rubric.
Severity is assigned by rule, not by how alarming a finding sounds. The rubric is published in the standards page and versioned with the report.
No single overall score is produced.
Composite scores compress unrelated evidence into one number that cannot be acted on. The report ends in an ordered list of work instead.
Audit output is never sold, aggregated, or published.
Reports belong to the buyer. Nothing from a run is used as a case study, a benchmark, or training data without a separate written agreement.
The runner never load-tests, floods, or brute-forces.
Requests are rate-limited and bounded by a stated crawl budget. The audit is designed to be indistinguishable from a careful visitor.
The runner never probes for vulnerabilities.
No injection attempts, no path traversal, no credential testing. This is an evidence audit, not a penetration test, and it is not a substitute for one.
Where a person is still required
An unattended product that pretends to cover these would be lying by omission. Each is named in the report itself, next to the results.
Accessibility conformance statements.
Automated checks find a minority of WCAG 2.1 issues. A conformance claim requires manual testing with assistive technology; this product does not make one and neither should a report derived from it.
Legal and regulatory interpretation.
The privacy domain records observed behaviour. Whether that behaviour complies with any given regulation is a question for a lawyer.
Content quality and editorial judgement.
Whether a page says the right thing to the right reader is not a machine question and is not answered here.
What to fix first, commercially.
The ledger orders by evidence and breadth. Only you know which route earns the revenue.
The fixing itself.
No code is written, patched, or deployed on your behalf as part of an audit.
Not buyable yet
Checkout is not open, there is no waitlist form, and no price is published. The launch page lists the gates that have to close first, and the launch feed records each one as it does.