ultra11y 5.3.3 → 5.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/marketplace.json +1 -1
- package/.claude-plugin/plugin.json +1 -1
- package/.codex-plugin/plugin.json +1 -1
- package/.opencode/plugins/ultra11y.js +1 -1
- package/package.json +1 -1
- package/scripts/ultra11y.mjs +1721 -1272
- package/skills/review-a11y/SKILL.md +1 -1
- package/skills/review-a11y/scripts/ultra11y.mjs +1721 -1272
- package/skills/ultra11y/SKILL.md +2 -2
- package/skills/ultra11y/references/ci.md +46 -4
- package/skills/ultra11y/references/judgment.md +50 -12
- package/skills/ultra11y/references/pages.md +13 -4
- package/skills/ultra11y/scripts/ultra11y.mjs +1721 -1272
- package/src/adjudicate.ts +229 -41
- package/src/audit.ts +163 -2
- package/src/cli.ts +216 -18
- package/src/ledger.ts +250 -0
- package/src/orchestrate-templates.ts +2 -2
- package/src/report.ts +24 -6
- package/src/rules/rendered.ts +39 -1
- package/src/types.ts +10 -3
package/skills/ultra11y/SKILL.md
CHANGED
|
@@ -4,7 +4,7 @@ description: "Use to AUDIT existing HTML/CSS/JSX against WCAG 2.2 AA accessibili
|
|
|
4
4
|
when_to_use: "Invoke when the user asks for an accessibility AUDIT or a formal deliverable: audit a repo, site or page against WCAG 2.2 AA or a country standard (RGAA, Section 508, EN 301 549); produce a dated conformance report, a criterion grid, a PRD backlog, or tickets (GitHub/GitLab/Jira); author accessible markup from scratch; wire the repo gate (init --hook/--ci) or a rendered-DOM capture pipeline. For a review of the code UNDER CHANGE (diff, branch, PR), use the `review-a11y` skill instead."
|
|
5
5
|
license: MIT
|
|
6
6
|
metadata:
|
|
7
|
-
version: 5.
|
|
7
|
+
version: 5.4.0
|
|
8
8
|
---
|
|
9
9
|
|
|
10
10
|
# ultra11y — audit WCAG 2.2 AA and write accessible markup
|
|
@@ -54,7 +54,7 @@ adjudicates. So the engine's clean run is a starting point, never a verdict —
|
|
|
54
54
|
> decides what its terms mean. An auditor block that cites `8.3.1` after reading it is
|
|
55
55
|
> grounded; one that cites it from memory is a guess wearing a reference. Before auditing
|
|
56
56
|
> against a country standard, run the plan (`ultra11y_method`): it tells you how many of its
|
|
57
|
-
> criteria no tool will decide for you — for RGAA, **
|
|
57
|
+
> criteria no tool will decide for you — for RGAA, **59 of 106**. See `references/mcp.md`.
|
|
58
58
|
> 6. **The FINAL rendered semantic HTML must be correct.** The engine sees only source; a
|
|
59
59
|
> component library (DSFR/MUI…) or `.vue`/`.svelte`/`.astro` SFC hides the real markup, so
|
|
60
60
|
> a green source audit is not proof. Verify the produced semantic HTML — install the
|
|
@@ -62,12 +62,54 @@ run and an RGAA run coexist rather than overwriting each other. The Markdown rep
|
|
|
62
62
|
through `check` before being uploaded, so CI cannot publish a report citing an invented
|
|
63
63
|
criterion.
|
|
64
64
|
|
|
65
|
-
##
|
|
65
|
+
## The verdict ledger (`ledger`) — a complete grid in CI, with no model
|
|
66
66
|
|
|
67
67
|
Of the 55 WCAG 2.2 AA criteria the engine decides a handful; **38 are judgment calls**, and
|
|
68
|
-
under RGAA **
|
|
69
|
-
|
|
70
|
-
|
|
68
|
+
under RGAA **59 of 106** can only ever derive `manual`. Inside a coding agent the agent rules on
|
|
69
|
+
them. In CI nobody does — so before the ledger, a CI grid was partial by construction and the
|
|
70
|
+
only way to close it was to pay for an agent pass on every run.
|
|
71
|
+
|
|
72
|
+
A **ledger** is a file of verdicts committed to the audited repository
|
|
73
|
+
(`.ultra11y/verdicts/<standard>.json`), each carrying its justification, its citations, and a
|
|
74
|
+
FINGERPRINT of the evidence it was ruled against:
|
|
75
|
+
|
|
76
|
+
```yaml
|
|
77
|
+
- uses: maxgfr/ultra11y@v5
|
|
78
|
+
with:
|
|
79
|
+
standard: rgaa
|
|
80
|
+
ledger: 'true' # or an explicit path
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
Replaying it is **a re-verification, not a cache hit**. Every stored verdict is rebuilt into an
|
|
84
|
+
ordinary adjudication over the evidence re-derived from the audit in front of it, and folded
|
|
85
|
+
through the same `applyAdjudication` — same coverage checks in both directions, same citation
|
|
86
|
+
matching against the criterion's own anchors, same content-level re-grounding. A verdict nobody
|
|
87
|
+
can prove is refused in CI exactly as it would be in a session.
|
|
88
|
+
|
|
89
|
+
Three properties are worth knowing:
|
|
90
|
+
|
|
91
|
+
- **A stale verdict is dropped, never carried.** When the code under a criterion changes, the
|
|
92
|
+
evidence changes with it, the fingerprint stops matching, and the criterion returns to « à
|
|
93
|
+
évaluer » saying so — with the date it was recorded and both evidence counts. A verdict that
|
|
94
|
+
silently outlives the code it described is the one thing a conformance deliverable cannot
|
|
95
|
+
afford. The log names every stale and every uncovered criterion, so a refresh pass knows
|
|
96
|
+
exactly what to re-adjudicate.
|
|
97
|
+
- **A reformatting is not a change.** The fingerprint ignores line numbers and the replay
|
|
98
|
+
re-anchors stored citations onto today's lines, so adding a comment at the top of a file does
|
|
99
|
+
not invalidate every verdict in it.
|
|
100
|
+
- **A missing or refused ledger never fails the job.** The criteria stay « à évaluer », which is
|
|
101
|
+
where they would have been without one.
|
|
102
|
+
|
|
103
|
+
To FILL it, run an adjudication with `ledger` set: the accepted verdicts are recorded on the way
|
|
104
|
+
out. Only accepted ones — a refused verdict in the ledger would be laundered back in on the next
|
|
105
|
+
replay. A defensible split is a weekly (or `workflow_dispatch`) job that adjudicates and commits
|
|
106
|
+
the refreshed ledger, and a pull-request job that only replays it.
|
|
107
|
+
|
|
108
|
+
## Adjudicating the judgment criteria in CI (`adjudicate`)
|
|
109
|
+
|
|
110
|
+
`adjudicate` is what FILLS the ledger — opt-in, in two modes. It runs **after** the replay, so a
|
|
111
|
+
paid pass only ever covers what the ledger did not: on a repository whose ledger is current, it
|
|
112
|
+
has nothing left to rule on and costs nothing.
|
|
71
113
|
|
|
72
114
|
```yaml
|
|
73
115
|
env:
|
|
@@ -71,6 +71,31 @@ test).
|
|
|
71
71
|
the **produced** HTML (see `render` / audit the build), otherwise the verdict is a false
|
|
72
72
|
negative.
|
|
73
73
|
|
|
74
|
+
## When the adjudicator has no shell (CI)
|
|
75
|
+
|
|
76
|
+
`verify --manual` writes the worklist **twice**, plus one brief per criterion:
|
|
77
|
+
|
|
78
|
+
| file | for |
|
|
79
|
+
|---|---|
|
|
80
|
+
| `ADJUDICATE.todo.json` · `ADJUDICATE.md` | a session **with a shell**: evidence inline, edit in place, fold yourself |
|
|
81
|
+
| `ADJUDICATE.verdicts.json` | the **only file to write** when you have no shell — verdicts, no evidence |
|
|
82
|
+
| `adjudicate/<criteriaId>.md` | one small brief per criterion: its evidence, its protocol, its citable tests |
|
|
83
|
+
|
|
84
|
+
Why the split exists, measured rather than assumed: under RGAA the worklist is 96 criteria
|
|
85
|
+
carrying 1590 harvested anchors — **536 KB** of JSON and **466 KB** of Markdown. An agent given
|
|
86
|
+
`Read/Grep/Glob/Edit/Write` cannot work with that: reading either document swamps its context,
|
|
87
|
+
and filling 96 verdicts inside half a megabyte is 96 exact-match edits. A real CI run spent 75
|
|
88
|
+
of 424 turns, hit 17 permission denials trying to run the commands the runbook prescribes, and
|
|
89
|
+
returned the file untouched — so the fail-closed fold discarded all 96 verdicts and every
|
|
90
|
+
criterion stayed « to assess », in a job that reported success. The verdicts file is **37 KB**
|
|
91
|
+
for the same worklist, and each brief is a few KB.
|
|
92
|
+
|
|
93
|
+
`verify --apply` accepts either file. Given the verdicts-only one it **re-derives the evidence
|
|
94
|
+
from the audit** (the worklist is a pure function of the audit) and then runs the identical
|
|
95
|
+
fold: same coverage checks in both directions, same citation matching against *that criterion's*
|
|
96
|
+
own anchors, same refusals. The smaller surface buys nothing from the gate — a `C` that cites
|
|
97
|
+
an anchor it was never shown is refused exactly as before.
|
|
98
|
+
|
|
74
99
|
## Adversarial verification of the non-conformities — `verify --report`
|
|
75
100
|
|
|
76
101
|
Unchanged: `node scripts/ultra11y.mjs verify --report audits/wcag-YYYY-MM-DD.md` writes `VERIFY.md`
|
|
@@ -92,7 +117,7 @@ Then `node scripts/ultra11y.mjs verify --apply VERIFY.todo.json` is green again
|
|
|
92
117
|
## Under a country standard, the worklist speaks that standard
|
|
93
118
|
|
|
94
119
|
`verify --manual --standard rgaa` keys the worklist by **RGAA criteria**, not WCAG success
|
|
95
|
-
criteria — which is the granularity that matters, since
|
|
120
|
+
criteria — which is the granularity that matters, since 59 of RGAA's 106 criteria can only
|
|
96
121
|
ever derive `manual`. Each item carries, inline:
|
|
97
122
|
|
|
98
123
|
- the criterion's **numbered tests, in full** (`11.2.1` … `11.2.6`) — what actually has to be
|
|
@@ -138,15 +163,27 @@ gate refuses:
|
|
|
138
163
|
|
|
139
164
|
| It returns | The gate does |
|
|
140
165
|
|---|---|
|
|
141
|
-
| `C`/`NA` with no justification |
|
|
142
|
-
| `NC` citing nothing, or a `file:line` that does not resolve against real source |
|
|
143
|
-
| `NC` citing a `normativeRef` belonging to another criterion |
|
|
144
|
-
| `manual` with no reason |
|
|
166
|
+
| `C`/`NA` with no justification | refuses THAT criterion |
|
|
167
|
+
| `NC` citing nothing, or a `file:line` that does not resolve against real source | refuses it |
|
|
168
|
+
| `NC` citing a `normativeRef` belonging to another criterion | refuses it |
|
|
169
|
+
| `manual` with no reason | refuses it |
|
|
145
170
|
| a verdict for a criterion nobody asked about | dropped before it reaches the gate |
|
|
146
|
-
| fewer verdicts than criteria (a
|
|
171
|
+
| fewer verdicts than criteria (a bounded `--max` run, a failed batch) | the missing ones stay « à évaluer »; the rest lands |
|
|
147
172
|
| all `manual` with reasons | **accepts** — that is a correct answer, not a failure |
|
|
148
173
|
|
|
149
|
-
|
|
174
|
+
**The fold is fail-closed per VERDICT, not per FILE.** No refused verdict is ever applied, and a
|
|
175
|
+
refusal costs its own criterion and nothing else: that criterion stays « à évaluer » carrying the
|
|
176
|
+
refusal as its reason, and every verdict that proved itself lands. This is not a loosening of the
|
|
177
|
+
gate — every check above is unchanged — it is a change of blast radius, and it was measured:
|
|
178
|
+
a CI run filled 95 of 96 verdicts correctly, one came back `null`, and the old file-level fold
|
|
179
|
+
discarded all 96, so a $16 adjudication published « à évaluer » across the whole grid in a job
|
|
180
|
+
that reported success.
|
|
181
|
+
|
|
182
|
+
`--strict` restores the all-or-nothing fold for a caller who genuinely wants it (a deliverable
|
|
183
|
+
signed off in one pass); there, a rejected adjudication leaves `audit-latest.json` untouched.
|
|
184
|
+
|
|
185
|
+
Whatever lands can be RECORDED with `--ledger <path>` and replayed on a later run with no model
|
|
186
|
+
in the loop — see `references/ci.md`.
|
|
150
187
|
|
|
151
188
|
**Strictly opt-in.** With no key the command explains itself and exits 2; nothing else in the
|
|
152
189
|
engine changes.
|
|
@@ -159,8 +196,9 @@ while an agent can open the cited files, which is what *link purpose in context*
|
|
|
159
196
|
Both skip themselves when `ANTHROPIC_API_KEY` is absent from the job environment, and both
|
|
160
197
|
absorb their own failure rather than taking the audit down with them. See `references/ci.md`.
|
|
161
198
|
|
|
162
|
-
`--max` bounds the spend and says out loud which criteria it did not submit. It
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
199
|
+
`--max` bounds the spend and says out loud which criteria it did not submit. It now composes
|
|
200
|
+
with `--apply`: the fold is per-verdict, so a bounded run lands what it covered and the rest stay
|
|
201
|
+
« à évaluer » — which is exactly what bounding spend is for. (It used to be refused outright, and
|
|
202
|
+
rightly so while the fold was all-or-nothing: the pair could only ever bill a full run to
|
|
203
|
+
guarantee a coverage failure. That refusal survives under `--apply --strict`, where it is still
|
|
204
|
+
true.)
|
|
@@ -292,10 +292,19 @@ criterion means one definite failure fired *somewhere* — not that the engine c
|
|
|
292
292
|
criterion. On a page where the failure did not fire, the engine knows "no definite failure
|
|
293
293
|
here", which is not "conforming": alt relevance, link purpose and reading order are still
|
|
294
294
|
nobody's verdict. So only the `static` criteria (the ones with an applicability predicate and
|
|
295
|
-
rules, `src/audit.ts` `APPLICABLE`) earn `C` by silence — under RGAA that is
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
295
|
+
rules, `src/audit.ts` `APPLICABLE`) earn `C` by silence — under RGAA that is **8.3 and 8.5, and
|
|
296
|
+
those two only**. 4.10, 8.4 and 8.6 look like they should qualify and do not: each is flagged
|
|
297
|
+
`judgment: true`, because the criterion asks more than its mapped success criterion does (8.6
|
|
298
|
+
asks whether the page title is *pertinent*; 2.4.2 only that a title exists), so `judgmentGuard`
|
|
299
|
+
turns their `C` into `manual` on purpose. Everything else stays « à évaluer » until `scan`, the
|
|
300
|
+
snapshot tier or an adjudication decides it. Before this rule a page with no images scored 100%
|
|
301
|
+
on « chaque image a-t-elle une alternative pertinente ? », a rate computed over criteria nobody
|
|
302
|
+
had assessed.
|
|
303
|
+
|
|
304
|
+
What a criterion CAN be decided as, without any of that, is a separate question: since the
|
|
305
|
+
engine learned to prove applicability (`src/audit.ts` `SUBJECT_MATTER`), a criterion whose
|
|
306
|
+
subject matter is absent from the whole scope closes as a justified `NA` — so a repository with
|
|
307
|
+
no media reports RGAA theme 4 as not applicable rather than as « à évaluer ».
|
|
299
308
|
|
|
300
309
|
A non-normative recommendation never flips a page criterion to `NC`, exactly as in core.
|
|
301
310
|
|