Recursive AI

Results and exports

All onehtml docs

A CheckReport (see checker.md) holds RuleResults. This page defines the result model and the two JSON exports. Both exports are deterministic: the same report gives the same bytes on every platform. Both are written with System.Text.Json writers; nothing uses reflection.

The result model

MemberMeaning
Rule, RuleIdthe rule that produced it, with its descriptor
OutcomeFailed, Passed, NeedsReview (static facts cannot decide), NeedsRender (rendered facts are needed), Inapplicable
Targetthe element (or text node); the null node for Inapplicable
Locatora selector unique in the document (generated on first use; null when none exists)
SourceBytesthe byte range of the target's start tag in the input, or none
Message, Findingsone sentence; and the attributes and values found, in attribute order
TaskImpact, TaskImpactReasonBlocksTask with interactive, form or dialog, or Cosmetic
CssOnlythe style context had interaction state, so the result reflects the CSS alone
FactsUsed, FactsMissingrendered facts a result used (for example layout text, text boxes); for NeedsRender, the facts to supply
SuggestedEditsedits, only where the rule names exactly one judgement-free fix

Labels live on the rule: Rule.Descriptor has the sources (ACT id, axe id, failure technique, or ours), the criteria (number, name, level, version introduced), BestPractice, Needs, the fix or the reason there is none, and tags; the rule also has a description, a help sentence and an impact. Descriptions and help texts are written for this library.

A suggested edit is a normal Edit against the checked snapshot, carrying meta with the rule id, the criteria and "suggested": true. Applying one is the caller's choice; Document.Apply and replay both accept them. aria-valid-attr-value, aria-prohibited-attr and listitem have no public fix: the correct edit depends on what the author meant (a mistyped value, a name meant for another element, a layout choice), so they report the problem and offer no suggestion.

Native JSON: onehtml-results version 1

Top level, in this order:

FieldValue
format"onehtml-results"
version1. A change that alters the meaning of a field raises it; new fields do not. Readers ignore unknown fields.
document{ "sha256": hex of the input bytes, "url": as given in the export options (omitted when none) }
styleContextviewportWidth, viewportHeight, media (screen/print), colorScheme, reducedMotion, contrast, forcedColors, cssOnly, and focus, hover, active, target (locators) when set
profilethe profile name (wcag-2.1-aa, all, …) or null
limitReacheda rule dropped results at MaxResultsPerRule
rulesthe rules that ran, in id order: id, description, help, impact, bestPractice, needs (static/rendered-facts), sources (kind: act/axe/failure-technique/own, id), criteria (number, handle, level, version), tags, fix, noFixReason
resultssee below
suggestedEditsone edit-format envelope (docs/edit-format.md) holding every distinct suggested edit, so replay needs one document

Each result: rule, outcome (failed, passed, needs-review, needs-render, inapplicable), message, target (null, or tag, locator, source with byte start and end), findings (attribute, value, message), taskImpact (blocksTask, reason), cssOnly, factsUsed, factsMissing, and suggested: a list of { "description", "edit": n } where n indexes suggestedEdits.edits. Results keep the report order: rule id, document order of the target, outcome.

Strings are UTF-8 JSON with the characters <, >, & and ' unescaped (the output is not meant to be embedded in HTML). Numbers are integers except the viewport size.

Axe-compatible JSON

The shape follows the results object that axe-core's public API documentation describes: violations, passes, incomplete and inapplicable, each a list of rule results with id, impact, tags, description, help, helpUrl and nodes; each node with html, impact, target, any, all, none and, for failures, failureSummary. Top-level testEngine, testRunner, testEnvironment and toolOptions are present; url is present when given.

The mapping is lossy, and these are its rules:

OursAxe-compatible
failedviolations
passedpasses
needs-review, needs-renderincomplete; the node's onehtml.outcome says which
inapplicableinapplicable (a rule with no target)
rule idthe axe id where an axe rule is a counterpart; ours otherwise, with our tags
rule impactthe rule's impact on violation and incomplete nodes; null on passes and inapplicable
target[locator] (one selector; no frames or shadow roots)
htmlthe target's start tag as written in the source, cut to MaxHtmlLength (300) characters; axe gives the serialised element
any/all/nonea failure or incomplete node has one check in any (id = rule id, data = findings, message); all and none are empty. Passed nodes have empty arrays. Our rules are not built from axe's checks, so nothing finer is available.
failureSummarythe result message and the findings' messages on separate lines (our wording, not axe's)
helpUrlthe Deque University page of the axe version for rules with an axe source; empty otherwise
description, helpour own sentences
not exportedtimestamp (it would make the output differ between runs), relatedNodes (always empty), frame and shadow paths
addedper node, onehtml: outcome, blocksTask, cssOnly, factsMissing, suggestedEdits (count)

A rule appears once per group it has results in; a rule with failures and passes is in both violations and passes. Within a group rules are in id order and nodes in document order.

SARIF

ToSarifJson writes SARIF 2.1.0; the mapping is in sarif.md.

Not provided yet

An EARL export. There is no reader for the native JSON; consumers read it with any JSON parser, and the edit envelopes inside it are read with EditJson.Parse.