Accessibility Fix Agent
@fmontes is already working on this.
Since Jul 27, 2026.
- Dominant language
- Java
- Stars
- 970
- Forks
- 486
- Avg merge
- 3d 33m
- Merged PRs (30d)
- 170
Description
Summary
dotCMS's first agent. It scans a traditional page for accessibility violations, fixes them in the VTL and CSS, and saves the result as a working version for a human to review and publish.
The loop is scan → locate → fix in code → save as working version → re-scan. Nothing reaches a live page without a person approving it.
Five workstreams:
- Scanner upgrade — full axe-core rule set plus a programmatic scan API. Sequenced first; nothing else can start until it returns structured violations.
- Locate — map a violation from the rendered DOM back to the VTL template, container, or CSS rule that produced it.
- Fix and verify — write the correction, save as a working version, re-scan to confirm.
- Agent UI — its own surface with the run trace and a before/after view per violation.
- Runtime and governance — runs on
@dotcms/aiwith host-side auth injection and an operation allow-list.
Background
The Page Health accessibility scanner (#35712) reports WCAG violations, but the value stops at the list. Someone still has to translate a rule id like button-name or color-contrast into an edit in a .vtl file they may not own.
On traditional pages the markup comes from Velocity templates and containers, and the styling from CSS files. An editor holding a WCAG report has no path to either, so remediation becomes a developer ticket and competes with feature work.
For accessibility specifically, most of that remediation is mechanical — a missing alt, a heading that skips a level, a contrast ratio a few points short, a button with no accessible name. It is well-defined, verifiable work that nonetheless sits in a queue.
Accessibility is also the right first job for an agent: the standard is objective (WCAG 2.1 AA via axe-core), the fix is verifiable by re-scanning, and the blast radius of a mechanical attribute fix is small. Scanners are commodity across the CMS market; automated in-product remediation is not.
Architecture
- Scan — the a11y scanner runs the complete axe-core rule set and exposes a programmatic API returning structured violations: rule id, DOM selector, impact, help text, affected nodes. The existing Page Health UI reads the same API, so in-product and agent results cannot diverge. The response shape is versioned, because the agent's fix logic is keyed to it.
- Locate — each violation is traced from its rendered-DOM selector back to the source that produced it: a VTL template, a container, a content field, or a CSS rule. Velocity renders by string concatenation and emits no source map, so this needed a feasibility spike — confirmed feasible (see below).
- Fix — the agent writes the correction into the VTL or CSS. Scoped to mechanical, high-confidence fix classes; anything requiring structural change or human judgement is flagged instead of attempted.
- Save — changes land as working versions of the affected templates, containers, and CSS assets. Never a live edit. Working-version semantics for these asset types were confirmed by spike.
- Verify — re-scan and report before/after violation counts, including detection of any newly introduced violations.
- Governance — runs on the
@dotcms/airuntime: auth is injected host-side and never reaches the model, writes are working-version-only, operations are allow-listed, and a human publishes.
Fix classes in scope
Mechanical, verifiable, low blast radius:
- Missing or empty
alton images - Missing accessible names on buttons, links, and form controls
- Heading-order violations
- Missing
langattribute - Missing form-label associations
- Colour-contrast failures resolvable by adjusting a CSS value
- Missing landmark roles and skip links
tabindexand focus-order problems- ARIA attribute misuse where the correct value is unambiguous
De-risking spikes — all confirmed
Three unknowns were resolved before committing to this shape. The agent writes code; it does not merely recommend.
- DOM-to-VTL mapping — feasible. The load-bearing unknown. A rendered node can be traced back to the template, container, or CSS rule that produced it. Had this failed, the epic would have degraded to guidance instead of edits.
- Regression safety — workable. There is a viable way to prove an agent edit did not break the rendered layout.
- Working-version semantics — confirmed for templates, containers, and CSS assets, so the review-then-publish loop holds for a change spanning several assets.
Each approach should be written up before build starts so the implementation inherits the method, not just the verdict.
Acceptance criteria
Scanner
- The a11y scanner runs the full axe-core rule set, not a subset
- A programmatic scan API returns structured violations with rule id, DOM selector, impact, help text, and affected nodes
- The Page Health UI reads the same API; results are identical in-product and to the agent
Agent
- For every in-scope fix class, a re-scan confirms the targeted violation resolved with no new violations introduced
- All writes are working versions; no code path can publish or modify live assets
- Violations traced to author-entered content, or requiring human judgement, are reported and not attempted
- When an edit targets an asset shared across multiple pages, the agent states the blast radius before writing
- Fixes that would alter rendered layout beyond the violation are refused
- Auth is injected host-side; the model never holds a token
UI
- A dedicated agent surface showing the run trace: what was scanned, what the agent decided, what it changed, and why
- Before/after view per violation, with the diff and the affected asset
- Accept/reject per fix, not all-or-nothing
- Clear separation of "fixed", "needs your judgement", and "content, not template"
- Audit trail: run id, assets touched, model used
Out of scope
- Headless front ends. The agent edits dotCMS-managed assets. Fixing a component in a customer's own repo is a different problem with different governance.
- Content-field fixes. If a violation traces to author-entered content rather than the template, the agent reports it and stops.
- Judgement-dependent fixes. Meaningful alt text for a photograph, deciding whether an image is decorative, reading-order rewrites. Flagged for a human, not guessed.
- Auto-publish. A human publishes, always.
- Site-wide batch remediation. One page per run. Batch is the follow-on once per-page accuracy is trusted.
- GEO and SEO fix agents. Same scan→fix→re-scan pattern and the intended generalization, but prove it on accessibility first.
- Publish-blocking on scan failure. A Page Health concern, not the agent's.
- The
@dotcms/airuntime and MCP server themselves. This epic is an application built on them.
Risks
- Shared-template blast radius. The largest open risk. A VTL template can back hundreds of pages, so a fix aimed at one page changes all of them. Mapping being feasible makes this sharper, not softer — the agent can find and edit the shared asset. It must state the blast radius before writing, and the UI must show which other pages are affected.
- A single broken layout destroys trust. Constrain v1 to attribute-level and CSS-value-level edits, working-version-only, and refuse anything structural.
- Accessibility overclaim. Automated tooling detects only a portion of WCAG failures. Nothing in the product or its documentation should imply that running the agent makes a page compliant. It fixes detected mechanical violations — no more.
- Runtime dependency. The agent has no fallback if
@dotcms/aicode execution is not ready.
Dependencies
- #35712 — the shipped Page Health a11y scanner. It has no programmatic API and does not run the full rule set, so this epic upgrades it rather than waiting on separate work. Sequenced first.
@dotcms/airuntime — code execution, host-side auth injection, operation allow-list.- MCP v2 — the tools the agent calls to read and write assets.
- Node serverless function capability, which the scanner already relies on.
Open questions
- Batch and scheduled runs. Out of scope for v1, but site-wide remediation is where the value compounds for large sites. Sequence as the follow-on once per-page accuracy is trusted.
- Coverage denominator. With the full axe-core rule set, the scanner reports many more violation types than the fix classes in scope. "Coverage" needs a defined denominator before it is reported anywhere, or the number will look like a regression when it is not.
References
- Page Health accessibility scanner: #35712
- Page Health GEO scanner (sibling): #35711
Open scanner defects that affect what the agent can rely on — worth resolving alongside the scanner upgrade:
- #36162 — scanner resolves the wrong site on multisite instances
- #36183 — scanner ignores UVE mode, language, persona, variant, and Time Machine
- #36364 — scanner fails with 403 behind IP allowlists / WAF
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Assessment
This issue has not been assessed yet.