Skip to content

QTI 3.0.1 authored-content ARIA accessibility, and honest disposition of the QTI model-coverage gaps

Status: accepted (2026-06-25)

The QTI 3.0.1 Coverage Map (conform-ed ADR-0013, emergent ADR-0028) read 224/5344 modelled with 358 silent gaps, blanket-triaged into 21 deferred gapGroups in docs/conformance/overlays/qti-v3.0.1.yaml. A line-by-line audit against conform-ed’s actual contracts showed that blanket deferral was the wrong story: the L2 reconciler joins the literal ASI XSD to conform-ed’s Zod by property name, and ~311 of the 358 “gaps” are constructs conform-ed does model under a different shape the name-join structurally cannot see — not absent capabilities. Leaving them as deferred (which means planned-but-unbuilt) is exactly the prose-drift the board exists to kill.

Give every one of the 358 gaps its honest disposition, per cluster:

Cluster Count Disposition Why
Expression / processing operators (qti-and, qti-sum, qti-set-outcome-value, …) 146 Normalise (conform-ed residues.normalisations) conform-ed models every operator + rule as a kind-discriminated union (expression/rules Zod properties); the XSD names them as elements of BranchRuleDType/LogicSingleDType/*ProcessingDType. Modelled differently, not absent.
Interaction / block children + structural-tail renames (qti-choice-interaction …, qti-map-entry, nested qti-assessment-section, qti-content-body, xml:lang) ~40 Normalise conform-ed models all 22 interactions as typed QtiContentFragment union members (emergent authors them), entries as arrays (mapEntries …), nested sections as typed children, xml:lang as lang/xmlLang.
Generic XHTML flow/inline vocabulary (a, p, div, table, img, video, …) ~110 Normalise, opaque-passthrough note conform-ed carries these via a generic opaque XML content node ({kind:"xml", name, attributes, children}) — lossless round-trip (the official corpus passes 315/327 through emergent’s pipeline), but not per-element schema. The normalisation note states this explicitly so modelled:yes is not misread as typed per-element validation.
ARIA attributes + role 47 BUILD (this ADR) conform-ed carried ARIA in a generic untyped ariaAttributes bag, never surfaced in authoring and never rendered onto QTI interaction DOM roots. Intersects the standing accessibility mandate — the one cluster that warrants real work, not reclassification.

The mechanism for the ~311 normalisations is conform-ed’s existing ADR-0017 machinery — structuralAliases bridging the Zod expression/rules/content/children/*Entries properties to the literal element names, plus a specRefOverride for xml:langxmlLang mirroring the existing xml:base one. Absorbed keys leave silentGaps and move into residues.normalisations; the corresponding emergent gapGroups are deleted (a silent gap, by definition unmodelled, can never be done — so honest closure lives upstream, not as an overlay relabel).

  1. conform-ed model — type the 47 ARIA attributes + role as a named ariaAttributes shape (value-sets for the closed enumerations: aria-checked etc.), bridged so the 47 ARIABaseDType/aria-* literal items become genuinely modelled:yes (typed, value-set checked) — not opaque.
  2. conform-ed delivery — the reference skin / runtime spreads the sanitized ariaAttributes bag onto each interaction’s render root, so ARIA authored on a QTI element (not just on generic HTML, which already renders) reaches assistive tech.
  3. conform-ed conformance — a new QTI-A11Y requirement: authored content / interactions MAY carry ARIA roles/states; they are preserved through emit and rendered to the delivery DOM. Cited against the ASI §2.13.3 WAI-ARIA attribute schema.
  4. emergent — surface ARIA fields in the item author panel across interaction kinds (full scope), and a [covers: qti:3.0.1:conf:…/QTI-A11Y-*] test proving ARIA on a QTI element survives emergent’s real ingest→serialize(→render) path.
  • Keep the 21 deferred gapGroups with sharpened reasons. Rejected: dishonest — the constructs are modelled (verified in processing-internal.ts / assessment-internal.ts).
  • Normalise ARIA like generic HTML (it uses the same generic bag). Rejected: honest at the model level but advances accessibility nothing, and dodges the standing mandate.
  • Build typed per-element XHTML schemas. Rejected: large blast radius, low value — the generic passthrough + emergent’s structured authoring already cover real needs.
  • Honest closure is conform-ed-side (normalisations) for ~311 items; the emergent overlay’s gapGroups shrink from 21 to (target) ~0, and silentGaps → ~0.
  • Cross-repo sequencing: conform-ed lands first; Anton FFs conform-ed main → CI publishes @dev; emergent re-pins (coverageMapSha256 refresh) and only then rewrites the overlay, builds the authoring UI, and adds the proof.
  • The normalisations rollup figure is the standing honesty disclosure that ~311 items are “modelled differently” (incl. opaque generic-content representation), not typed per-node.