ultra11y 4.4.1 → 4.5.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.
@@ -10,7 +10,7 @@
10
10
  "name": "ultra11y",
11
11
  "source": "./",
12
12
  "description": "Automatic accessibility review of the code under change before every commit, push and pull request, plus full WCAG 2.2 AA / RGAA audits and conformance deliverables on demand.",
13
- "version": "4.4.1",
13
+ "version": "4.5.0",
14
14
  "author": { "name": "maxgfr" },
15
15
  "homepage": "https://github.com/maxgfr/ultra11y",
16
16
  "repository": "https://github.com/maxgfr/ultra11y",
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "ultra11y",
3
3
  "description": "Accessibility for WCAG 2.2 AA and country standards (RGAA…): an automatic review of the code under change before every commit, push and pull request, plus full audits and conformance deliverables on demand.",
4
- "version": "4.4.1",
4
+ "version": "4.5.0",
5
5
  "author": { "name": "maxgfr" },
6
6
  "homepage": "https://github.com/maxgfr/ultra11y",
7
7
  "repository": "https://github.com/maxgfr/ultra11y",
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "ultra11y",
3
3
  "description": "Accessibility for WCAG 2.2 AA and country standards (RGAA…): an automatic review of the code under change before every commit, push and pull request, plus full audits and conformance deliverables on demand.",
4
- "version": "4.4.1",
4
+ "version": "4.5.0",
5
5
  "author": { "name": "maxgfr", "url": "https://github.com/maxgfr" },
6
6
  "homepage": "https://github.com/maxgfr/ultra11y",
7
7
  "repository": "https://github.com/maxgfr/ultra11y",
@@ -27,7 +27,7 @@ import { fileURLToPath } from "node:url";
27
27
  // Ownership marker: `ultra11y install --opencode` refuses to overwrite a plugin file that
28
28
  // does not carry it, so a user's own ultra11y.js is never clobbered.
29
29
  const ULTRA11Y_OPENCODE_PLUGIN = "ULTRA11Y_OPENCODE_PLUGIN";
30
- const ULTRA11Y_PLUGIN_VERSION = "4.4.1";
30
+ const ULTRA11Y_PLUGIN_VERSION = "4.5.0";
31
31
 
32
32
  const here = dirname(fileURLToPath(import.meta.url));
33
33
 
package/README.md CHANGED
@@ -125,7 +125,7 @@ claude mcp add ultra11y -- npx -y ultra11y mcp
125
125
  **As a GitHub Action** — audits the PR diff and, optionally, the served pages:
126
126
 
127
127
  ```yaml
128
- - uses: maxgfr/ultra11y@v3
128
+ - uses: maxgfr/ultra11y@v4
129
129
  with: { since: auto, standard: rgaa, fail-on: blocking }
130
130
  ```
131
131
 
@@ -383,7 +383,7 @@ a test). See [`CONTRIBUTING.md`](CONTRIBUTING.md) and `skills/ultra11y/reference
383
383
  block. It re-decides nothing and invents no format, so a page sheet and the compliance report
384
384
  cannot disagree; `check` gates it against invented criteria like any other report.
385
385
  See `references/pages.md`.
386
- - **A shipped GitHub Action** — `uses: maxgfr/ultra11y@main` audits the **code** (PR diff) and,
386
+ - **A shipped GitHub Action** — `uses: maxgfr/ultra11y@v4` audits the **code** (PR diff) and,
387
387
  optionally, the **pages** (it can start your app, wait for it, then scan real URLs or your
388
388
  declared sample). The engine ships inside the action, so there is nothing to install and no
389
389
  `setup-node`. The gate runs **last**, after SARIF, annotations, the summary, the sticky PR
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ultra11y",
3
- "version": "4.4.1",
3
+ "version": "4.5.0",
4
4
  "description": "Audit HTML/CSS/JSX against WCAG 2.2 AA and produce a dated compliance report, or author/review accessible markup (native-HTML-first). A skills.sh agent skill: an install-free static engine (bundled, no npm install) plus the agent's judgment, with check/verify gates against hallucinated non-conformities, and pluggable in-repo standards packs (RGAA France first; Section 508 & EN 301 549 next).",
5
5
  "type": "module",
6
6
  "packageManager": "pnpm@10.33.0",
@@ -13,6 +13,7 @@
13
13
  // node scripts/build-standards.mjs --offline # same (alias; the snapshots are always local)
14
14
  // node scripts/build-standards.mjs --refresh <dir> # re-derive the vendored AA snapshot from a w3c/wcag checkout
15
15
  // node scripts/build-standards.mjs --refresh-universe # re-fetch (network) the vendored FULL SC universe (all levels + removed 4.1.1)
16
+ // node scripts/build-standards.mjs --refresh-core # re-derive the shipped AA snapshot from that universe (no checkout, no network)
16
17
  // node scripts/build-standards.mjs --refresh-fr # re-fetch (network) the vendored French SC/guideline/principle titles
17
18
  // node scripts/build-standards.mjs --refresh-text # re-fetch (network) the vendored NORMATIVE SC text + the WCAG glossary
18
19
  import { writeFileSync, readFileSync, readdirSync, existsSync, mkdirSync } from "node:fs";
@@ -398,6 +399,49 @@ async function deriveFr() {
398
399
  return out;
399
400
  }
400
401
 
402
+ // ---------------------------------------------------------------------------
403
+ // Core mode: the shipped AA snapshot, derived from the vendored UNIVERSE.
404
+ //
405
+ // `deriveSnapshot` above reads a local w3c/wcag checkout, which is why it was the one
406
+ // vendored source the daily refresh could not touch — a new or renamed success criterion
407
+ // would land in the universe and never reach the shipped core.
408
+ //
409
+ // It needs no checkout and no extra network: `--refresh-universe` already fetches the same
410
+ // numbering and titles for EVERY criterion, and records which are `core-AA`. The AA slice
411
+ // is that filter. Byte-identical to what the checkout produces (tests/wcag-core-sync).
412
+ //
413
+ // node scripts/build-standards.mjs --refresh-core
414
+ // ---------------------------------------------------------------------------
415
+ function deriveCoreFromUniverse() {
416
+ if (!existsSync(VENDOR_UNIVERSE)) {
417
+ console.error(`build-standards --refresh-core: missing ${VENDOR_UNIVERSE}. Run: node scripts/build-standards.mjs --refresh-universe`);
418
+ process.exit(1);
419
+ }
420
+ const universe = JSON.parse(readFileSync(VENDOR_UNIVERSE, "utf8"));
421
+ const aa = universe.criteria.filter((c) => c.status === "core-AA").map(({ status, ...rest }) => rest);
422
+ if (!aa.length) {
423
+ console.error(`build-standards --refresh-core: ${VENDOR_UNIVERSE} classifies no criterion as core-AA — refusing to write an empty core.`);
424
+ process.exit(1);
425
+ }
426
+ const used = new Set(aa.map((c) => c.guideline));
427
+ const snapshot = {
428
+ wcagVersion: universe.wcagVersion,
429
+ source: universe.source,
430
+ criteriaSource: universe.criteriaSource,
431
+ principles: universe.principles.map((p) => ({ number: p.number, title: p.title })),
432
+ guidelines: universe.guidelines.filter((g) => used.has(g.number)).map((g) => ({ number: g.number, title: g.title })),
433
+ criteria: aa,
434
+ };
435
+ return JSON.stringify(snapshot, null, 2) + "\n";
436
+ }
437
+
438
+ function refreshCore() {
439
+ const text = deriveCoreFromUniverse();
440
+ mkdirSync(dirname(VENDOR), { recursive: true });
441
+ writeFileSync(VENDOR, text);
442
+ console.log(`build-standards --refresh-core: ${JSON.parse(text).criteria.length} A/AA criteria derived from the vendored universe → ${VENDOR}`);
443
+ }
444
+
401
445
  // ---------------------------------------------------------------------------
402
446
  // Text mode: the NORMATIVE WORDING of every shipped success criterion, plus the terms
403
447
  // WCAG itself defines.
@@ -830,6 +874,7 @@ async function main() {
830
874
  const refreshIdx = process.argv.indexOf("--refresh");
831
875
  if (refreshIdx !== -1) deriveSnapshot(process.argv[refreshIdx + 1]);
832
876
  if (process.argv.includes("--refresh-universe")) await deriveUniverse();
877
+ if (process.argv.includes("--refresh-core")) refreshCore();
833
878
  if (process.argv.includes("--refresh-fr")) await deriveFr();
834
879
  if (process.argv.includes("--refresh-text")) await deriveText();
835
880
  build();
@@ -6,7 +6,7 @@ import { join as join50, relative as relative5, resolve as resolve15, sep as sep
6
6
  import { fileURLToPath as fileURLToPath5, pathToFileURL as pathToFileURL3 } from "url";
7
7
 
8
8
  // src/types.ts
9
- var VERSION = "4.4.1";
9
+ var VERSION = "4.5.0";
10
10
  var SCHEMA_VERSION = 2;
11
11
 
12
12
  // src/audit.ts
@@ -2289,7 +2289,7 @@ var wcag_universe_default = {
2289
2289
  wcagVersion: "2.2",
2290
2290
  source: "https://www.w3.org/TR/WCAG22/",
2291
2291
  criteriaSource: "https://github.com/w3c/wcag",
2292
- provenance: "Full WCAG 2.x SC universe (all levels incl. AAA, and the removed 4.1.1 Parsing) fetched from raw.githubusercontent.com/w3c/wcag@main on 2026-08-14 via `node scripts/build-standards.mjs --refresh-universe`. Classification: core-AA = ships in src/data/wcag.json (the shipped WCAG 2.2 AA core); out-of-core = WCAG AAA; removed = obsolete (4.1.1).",
2292
+ provenance: "Full WCAG 2.x SC universe (all levels incl. AAA, and the removed 4.1.1 Parsing) fetched from raw.githubusercontent.com/w3c/wcag@main on 2026-08-17 via `node scripts/build-standards.mjs --refresh-universe`. Classification: core-AA = ships in src/data/wcag.json (the shipped WCAG 2.2 AA core); out-of-core = WCAG AAA; removed = obsolete (4.1.1).",
2293
2293
  criteria: [
2294
2294
  {
2295
2295
  id: "1.1.1",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "source": "https://www.w3.org/Translations/WCAG22-fr/",
3
- "fetchedAt": "2026-08-14",
3
+ "fetchedAt": "2026-08-17",
4
4
  "principles": {
5
5
  "1": "Perceptible",
6
6
  "2": "Utilisable",
@@ -2,7 +2,7 @@
2
2
  "wcagVersion": "2.2",
3
3
  "source": "https://www.w3.org/TR/WCAG22/",
4
4
  "criteriaSource": "https://github.com/w3c/wcag",
5
- "provenance": "Full WCAG 2.x SC universe (all levels incl. AAA, and the removed 4.1.1 Parsing) fetched from raw.githubusercontent.com/w3c/wcag@main on 2026-08-14 via `node scripts/build-standards.mjs --refresh-universe`. Classification: core-AA = ships in src/data/wcag.json (the shipped WCAG 2.2 AA core); out-of-core = WCAG AAA; removed = obsolete (4.1.1).",
5
+ "provenance": "Full WCAG 2.x SC universe (all levels incl. AAA, and the removed 4.1.1 Parsing) fetched from raw.githubusercontent.com/w3c/wcag@main on 2026-08-17 via `node scripts/build-standards.mjs --refresh-universe`. Classification: core-AA = ships in src/data/wcag.json (the shipped WCAG 2.2 AA core); out-of-core = WCAG AAA; removed = obsolete (4.1.1).",
6
6
  "principles": [
7
7
  {
8
8
  "number": 1,
@@ -2,7 +2,7 @@
2
2
  "source": "https://github.com/w3c/wcag",
3
3
  "understanding": "https://www.w3.org/WAI/WCAG22/Understanding/",
4
4
  "license": "W3C Document License (code samples: W3C Software License)",
5
- "fetchedAt": "2026-08-14",
5
+ "fetchedAt": "2026-08-17",
6
6
  "criteria": {
7
7
  "1.1.1": {
8
8
  "slug": "non-text-content",
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "source": "https://github.com/w3c/wcag",
3
3
  "license": "W3C Document License",
4
- "fetchedAt": "2026-08-14",
4
+ "fetchedAt": "2026-08-17",
5
5
  "criteria": {
6
6
  "1.1.1": "All non-text content that is presented to the user has a text alternative that serves the equivalent purpose, except for the situations listed below.\nControls, Input:\nIf non-text content is a control or accepts user input, then it has a name that describes its purpose. (Refer to Success Criterion 4.1.2 for additional requirements for controls and content that accepts user input.)\nTime-Based Media:\nIf non-text content is time-based media, then text alternatives at least provide descriptive identification of the non-text content. (Refer to Guideline 1.2 for additional requirements for media.)\nTest:\nIf non-text content is a test or exercise that would be invalid if presented in text, then text alternatives at least provide descriptive identification of the non-text content.\nSensory:\nIf non-text content is primarily intended to create a specific sensory experience, then text alternatives at least provide descriptive identification of the non-text content.\nCAPTCHA:\nIf the purpose of non-text content is to confirm that content is being accessed by a person rather than a computer, then text alternatives that identify and describe the purpose of the non-text content are provided, and alternative forms of CAPTCHA using output modes for different types of sensory perception are provided to accommodate different disabilities.\nDecoration, Formatting, Invisible:\nIf non-text content is pure decoration, is used only for visual formatting, or is not presented to users, then it is implemented in a way that it can be ignored by assistive technology.",
7
7
  "1.2.1": "For prerecorded audio-only and prerecorded video-only media, the following are true, except when the audio or video is a media alternative for text and is clearly labeled as such:\nPrerecorded Audio-only:\nAn alternative for time-based media is provided that presents equivalent information for prerecorded audio-only content.\nPrerecorded Video-only:\nEither an alternative for time-based media or an audio track is provided that presents equivalent information for prerecorded video-only content.",
@@ -5,7 +5,7 @@ when_to_use: "Invoke WITHOUT being asked whenever changed front-end code should
5
5
  allowed-tools: Bash(node ${CLAUDE_SKILL_DIR}/scripts/ultra11y.mjs *), Read, Grep, Glob
6
6
  license: MIT
7
7
  metadata:
8
- version: 4.4.1
8
+ version: 4.5.0
9
9
  ---
10
10
 
11
11
  # review-a11y — accessibility review of changed code
@@ -6,7 +6,7 @@ import { join as join50, relative as relative5, resolve as resolve15, sep as sep
6
6
  import { fileURLToPath as fileURLToPath5, pathToFileURL as pathToFileURL3 } from "url";
7
7
 
8
8
  // src/types.ts
9
- var VERSION = "4.4.1";
9
+ var VERSION = "4.5.0";
10
10
  var SCHEMA_VERSION = 2;
11
11
 
12
12
  // src/audit.ts
@@ -2289,7 +2289,7 @@ var wcag_universe_default = {
2289
2289
  wcagVersion: "2.2",
2290
2290
  source: "https://www.w3.org/TR/WCAG22/",
2291
2291
  criteriaSource: "https://github.com/w3c/wcag",
2292
- provenance: "Full WCAG 2.x SC universe (all levels incl. AAA, and the removed 4.1.1 Parsing) fetched from raw.githubusercontent.com/w3c/wcag@main on 2026-08-14 via `node scripts/build-standards.mjs --refresh-universe`. Classification: core-AA = ships in src/data/wcag.json (the shipped WCAG 2.2 AA core); out-of-core = WCAG AAA; removed = obsolete (4.1.1).",
2292
+ provenance: "Full WCAG 2.x SC universe (all levels incl. AAA, and the removed 4.1.1 Parsing) fetched from raw.githubusercontent.com/w3c/wcag@main on 2026-08-17 via `node scripts/build-standards.mjs --refresh-universe`. Classification: core-AA = ships in src/data/wcag.json (the shipped WCAG 2.2 AA core); out-of-core = WCAG AAA; removed = obsolete (4.1.1).",
2293
2293
  criteria: [
2294
2294
  {
2295
2295
  id: "1.1.1",
@@ -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: 4.4.1
7
+ version: 4.5.0
8
8
  ---
9
9
 
10
10
  # ultra11y — audit WCAG 2.2 AA and write accessible markup
@@ -15,7 +15,7 @@ permissions:
15
15
  steps:
16
16
  - uses: actions/checkout@v4
17
17
  with: { fetch-depth: 0 } # the diff gate needs the base ref
18
- - uses: maxgfr/ultra11y@v3 # or pin the exact version, as `init --ci` does
18
+ - uses: maxgfr/ultra11y@v4 # or pin the exact version, as `init --ci` does
19
19
  with:
20
20
  since: auto # the PR's base branch
21
21
  standard: rgaa
@@ -27,7 +27,7 @@ steps:
27
27
  Page by page, with a real browser, in the same step:
28
28
 
29
29
  ```yaml
30
- - uses: maxgfr/ultra11y@v3
30
+ - uses: maxgfr/ultra11y@v4
31
31
  with:
32
32
  standard: rgaa
33
33
  start: npm run start
@@ -66,7 +66,7 @@ partial by construction. `adjudicate` closes that — opt-in, in two modes.
66
66
  env:
67
67
  ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }} # the job's env, never an input
68
68
  steps:
69
- - uses: maxgfr/ultra11y@v3
69
+ - uses: maxgfr/ultra11y@v4
70
70
  with:
71
71
  standard: rgaa
72
72
  adjudicate: api # or `agent`
@@ -89,14 +89,43 @@ open the cited files and read around them, which is what *link purpose **in cont
89
89
  asks for. Expect the API mode to answer `manual` more often; that is it being honest, not
90
90
  broken. The metric that separates them is the count of criteria left to assess.
91
91
 
92
- **Both modes read `ANTHROPIC_API_KEY` from the job environment, never from an input** — a
92
+ **Both modes read their credential from the job environment, never from an input** — a
93
93
  composite action's steps inherit it, and a secret that never travels through an input is one
94
- fewer secret to leak into a log. When the key is absent the tier **skips itself**: the job stays
94
+ fewer secret to leak into a log. When it is absent the tier **skips itself**: the job stays
95
95
  green, the report is still written, and the criteria stay « à évaluer ». That is not an edge
96
96
  case — it is precisely what a **fork's pull request** looks like, where secrets are never
97
97
  exposed. An adjudication that fails mid-flight (a rate-limited batch refuses the whole fold) is
98
98
  absorbed the same way, because losing the verdicts must never cost you the audit.
99
99
 
100
+ **The two modes do not take the same credential.** `api` runs `judge`, which speaks `x-api-key`
101
+ to `api.anthropic.com`; nothing else stands in for `ANTHROPIC_API_KEY` there. `agent` shells out
102
+ to `claude-code-action`, which accepts **either** an API key **or** the OAuth token a Pro/Max
103
+ subscriber mints with `claude setup-token` — so a repository that already runs Claude Code in CI
104
+ and holds only `CLAUDE_CODE_OAUTH_TOKEN` can adjudicate without buying API credit. Set whichever
105
+ you have; set both and the API key wins.
106
+
107
+ ```yaml
108
+ env:
109
+ # `agent` takes either of these. `api` takes only the first.
110
+ ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
111
+ CLAUDE_CODE_OAUTH_TOKEN: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}
112
+ ```
113
+
114
+ Asking for `api` with only the OAuth token is a warning, not a failure — the tier skips and says
115
+ which credential it would have needed, rather than failing a request the Messages API was always
116
+ going to reject.
117
+
118
+ **`agent` cannot run on every event.** `claude-code-action` resolves the GitHub event context
119
+ *before* it reads the prompt, and throws `Unsupported event type: <event>` on anything outside
120
+ `pull_request`, `pull_request_target`, `pull_request_review(_comment)`, `issues`,
121
+ `issue_comment`, `workflow_dispatch`, `repository_dispatch`, `schedule` and `workflow_run`.
122
+ **`push` is not in that list** — and `push` is exactly what an accessibility gate runs on. So the
123
+ tier checks the event and degrades to a warning there, the same way it does without a credential:
124
+ an a11y job must never go red for a reason that has nothing to do with accessibility. Put the
125
+ agent tier on the `pull_request` or `schedule` job, or use `adjudicate: api`, which has no such
126
+ constraint. The action also hands `claude-code-action` the job's own `GITHUB_TOKEN`, so this tier
127
+ never obliges you to add `id-token: write` for a step that makes no GitHub API call.
128
+
100
129
  **Cost is per run and does not amortise.** Roughly $0.20 for a WCAG run and $0.50 for RGAA at
101
130
  Sonnet pricing — the worklist is re-sent whole each time and no batch is large enough to earn
102
131
  prompt caching. On a busy repository that is real money per push; this belongs on the default
@@ -235,7 +264,7 @@ The action audits the **code** and, when you point it at a served app, the **pag
235
264
  list can come from three places, and none of them has to be written by hand:
236
265
 
237
266
  ```yaml
238
- - uses: maxgfr/ultra11y@v3
267
+ - uses: maxgfr/ultra11y@v4
239
268
  with:
240
269
  standard: rgaa
241
270
  start: npm run start
@@ -262,6 +291,40 @@ Two surfaces come out of it:
262
291
 
263
292
  A run with no page in scope is not a failure: the report step says so and the job carries on.
264
293
 
294
+ ### Pages behind a login (`storage-state`)
295
+
296
+ The pages worth auditing are rarely the public ones. A form-heavy funnel, a dashboard, a
297
+ back-office — all of them sit behind a sign-in, and a scan that reaches them anonymously does
298
+ not fail: it records **the sign-in screen** under the name of the page you asked for. That is
299
+ the worst failure mode an accessibility report has, because the document looks complete.
300
+
301
+ Produce a Playwright `storageState` in the job — most repositories already have one, written by
302
+ the setup project their E2E suite depends on — and hand the action its path:
303
+
304
+ ```yaml
305
+ - run: npx playwright test --project=setup # your existing login, writing the session file
306
+ - uses: maxgfr/ultra11y@v4
307
+ with:
308
+ standard: rgaa
309
+ sample: 'true'
310
+ storage-state: test-results/.auth/user.json
311
+ ```
312
+
313
+ The path is forwarded to Playwright as a path and never read by the engine, so a session file
314
+ cannot end up in the job log. A per-page `storageState` in `.ultra11yrc.json` **overrides** this
315
+ one, so a sample can mix public and signed-in pages in a single run.
316
+
317
+ Two things worth knowing about an authenticated scan:
318
+
319
+ - **The click probe is off.** With a session loaded, a click can trigger a real mutation on a
320
+ real record. The live-region probe still fills inputs and re-measures, but it does not click
321
+ buttons unless you set `interact-clicks: 'true'`. (Buttons whose accessible name reads
322
+ destructive are never clicked, session or not.)
323
+ - **A session is not a state machine.** Being signed in gets you past the login; it does not put
324
+ a multi-step funnel on step 4. If a route is guarded by application state, seed that state in
325
+ the job before scanning — otherwise the page redirects and you are back to auditing the wrong
326
+ screen under the right name.
327
+
265
328
  ## What the artifact looks like when you open it
266
329
 
267
330
  `audits/` used to hold JSON, SARIF and Markdown: everything a machine needs and nothing that
@@ -395,9 +458,9 @@ one job — the code diff, then the served pages — died on a `409 Conflict`, w
395
458
  written and never uploaded. Pass `artifact-name` on each invocation:
396
459
 
397
460
  ```yaml
398
- - uses: maxgfr/ultra11y@v3
461
+ - uses: maxgfr/ultra11y@v4
399
462
  with: { since: auto, artifact-name: a11y-code }
400
- - uses: maxgfr/ultra11y@v3
463
+ - uses: maxgfr/ultra11y@v4
401
464
  with: { crawl: http://localhost:3000, artifact-name: a11y-pages }
402
465
  ```
403
466
 
@@ -6,7 +6,7 @@ import { join as join50, relative as relative5, resolve as resolve15, sep as sep
6
6
  import { fileURLToPath as fileURLToPath5, pathToFileURL as pathToFileURL3 } from "url";
7
7
 
8
8
  // src/types.ts
9
- var VERSION = "4.4.1";
9
+ var VERSION = "4.5.0";
10
10
  var SCHEMA_VERSION = 2;
11
11
 
12
12
  // src/audit.ts
@@ -2289,7 +2289,7 @@ var wcag_universe_default = {
2289
2289
  wcagVersion: "2.2",
2290
2290
  source: "https://www.w3.org/TR/WCAG22/",
2291
2291
  criteriaSource: "https://github.com/w3c/wcag",
2292
- provenance: "Full WCAG 2.x SC universe (all levels incl. AAA, and the removed 4.1.1 Parsing) fetched from raw.githubusercontent.com/w3c/wcag@main on 2026-08-14 via `node scripts/build-standards.mjs --refresh-universe`. Classification: core-AA = ships in src/data/wcag.json (the shipped WCAG 2.2 AA core); out-of-core = WCAG AAA; removed = obsolete (4.1.1).",
2292
+ provenance: "Full WCAG 2.x SC universe (all levels incl. AAA, and the removed 4.1.1 Parsing) fetched from raw.githubusercontent.com/w3c/wcag@main on 2026-08-17 via `node scripts/build-standards.mjs --refresh-universe`. Classification: core-AA = ships in src/data/wcag.json (the shipped WCAG 2.2 AA core); out-of-core = WCAG AAA; removed = obsolete (4.1.1).",
2293
2293
  criteria: [
2294
2294
  {
2295
2295
  id: "1.1.1",
@@ -2,7 +2,7 @@
2
2
  "wcagVersion": "2.2",
3
3
  "source": "https://www.w3.org/TR/WCAG22/",
4
4
  "criteriaSource": "https://github.com/w3c/wcag",
5
- "provenance": "Full WCAG 2.x SC universe (all levels incl. AAA, and the removed 4.1.1 Parsing) fetched from raw.githubusercontent.com/w3c/wcag@main on 2026-08-14 via `node scripts/build-standards.mjs --refresh-universe`. Classification: core-AA = ships in src/data/wcag.json (the shipped WCAG 2.2 AA core); out-of-core = WCAG AAA; removed = obsolete (4.1.1).",
5
+ "provenance": "Full WCAG 2.x SC universe (all levels incl. AAA, and the removed 4.1.1 Parsing) fetched from raw.githubusercontent.com/w3c/wcag@main on 2026-08-17 via `node scripts/build-standards.mjs --refresh-universe`. Classification: core-AA = ships in src/data/wcag.json (the shipped WCAG 2.2 AA core); out-of-core = WCAG AAA; removed = obsolete (4.1.1).",
6
6
  "criteria": [
7
7
  {
8
8
  "id": "1.1.1",
package/src/types.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  // Single source of truth for shared types + the embedded version. sync-version.mjs
2
2
  // rewrites VERSION at release time (kept in lockstep with package.json + SKILL.md).
3
- export const VERSION = "4.4.1";
3
+ export const VERSION = "4.5.0";
4
4
  export const SCHEMA_VERSION = 2;
5
5
 
6
6
  // Lang = the UI FRAME's language — the `L` localization tables each module (report,