ultra11y 2.31.2 → 2.31.3
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/build-pack-rgaa.mjs +31 -0
- package/scripts/ultra11y.mjs +183 -56
- package/scripts/vendor/wcag-2.2-fr.json +1 -1
- package/scripts/vendor/wcag-2.2-sc-universe.json +1 -1
- package/skills/review-a11y/SKILL.md +1 -1
- package/skills/review-a11y/scripts/ultra11y.mjs +183 -56
- package/skills/ultra11y/SKILL.md +10 -5
- package/skills/ultra11y/references/pages.md +7 -3
- package/skills/ultra11y/scripts/ultra11y.mjs +183 -56
- package/src/adjudicate.ts +77 -12
- package/src/auditor.ts +2 -2
- package/src/cli.ts +26 -10
- package/src/data/standards/rgaa.json +10 -5
- package/src/data/wcag-universe.json +1 -1
- package/src/llm.ts +23 -2
- package/src/pages-report.ts +51 -12
- package/src/prd.ts +3 -2
- package/src/report.ts +70 -19
- package/src/standards/derive.ts +33 -2
- package/src/standards/pack.ts +13 -0
- package/src/standards/types.ts +13 -0
- package/src/types.ts +1 -1
package/scripts/ultra11y.mjs
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
// src/cli.ts
|
|
4
|
-
import { realpathSync as realpathSync6, writeFileSync as writeFileSync17, mkdirSync as mkdirSync15, existsSync as existsSync31, readFileSync as readFileSync27, appendFileSync } from "fs";
|
|
4
|
+
import { realpathSync as realpathSync6, writeFileSync as writeFileSync17, mkdirSync as mkdirSync15, existsSync as existsSync31, readFileSync as readFileSync27, appendFileSync, copyFileSync as copyFileSync4 } from "fs";
|
|
5
5
|
import { join as join45, relative as relative5, sep as sep7, dirname as dirname14 } from "path";
|
|
6
6
|
import { fileURLToPath as fileURLToPath5, pathToFileURL as pathToFileURL3 } from "url";
|
|
7
7
|
|
|
8
8
|
// src/types.ts
|
|
9
|
-
var VERSION = "2.31.
|
|
9
|
+
var VERSION = "2.31.3";
|
|
10
10
|
var SCHEMA_VERSION = 2;
|
|
11
11
|
|
|
12
12
|
// src/audit.ts
|
|
@@ -1197,7 +1197,7 @@ var wcag_universe_default = {
|
|
|
1197
1197
|
wcagVersion: "2.2",
|
|
1198
1198
|
source: "https://www.w3.org/TR/WCAG22/",
|
|
1199
1199
|
criteriaSource: "https://github.com/w3c/wcag",
|
|
1200
|
-
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-
|
|
1200
|
+
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-12 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).",
|
|
1201
1201
|
criteria: [
|
|
1202
1202
|
{
|
|
1203
1203
|
id: "1.1.1",
|
|
@@ -41345,7 +41345,8 @@ var rgaa_default = {
|
|
|
41345
41345
|
wcag: ["1.4.2"],
|
|
41346
41346
|
appliesTo: {
|
|
41347
41347
|
ruleIds: ["autoplay-media", "axe:no-autoplay-audio"]
|
|
41348
|
-
}
|
|
41348
|
+
},
|
|
41349
|
+
judgment: true
|
|
41349
41350
|
},
|
|
41350
41351
|
{
|
|
41351
41352
|
id: "4.11",
|
|
@@ -41984,7 +41985,8 @@ var rgaa_default = {
|
|
|
41984
41985
|
wcag: ["3.1.1"],
|
|
41985
41986
|
appliesTo: {
|
|
41986
41987
|
ruleIds: ["axe:html-lang-valid", "lang-invalid"]
|
|
41987
|
-
}
|
|
41988
|
+
},
|
|
41989
|
+
judgment: true
|
|
41988
41990
|
},
|
|
41989
41991
|
{
|
|
41990
41992
|
id: "8.5",
|
|
@@ -42020,7 +42022,8 @@ var rgaa_default = {
|
|
|
42020
42022
|
wcag: ["2.4.2"],
|
|
42021
42023
|
appliesTo: {
|
|
42022
42024
|
ruleIds: []
|
|
42023
|
-
}
|
|
42025
|
+
},
|
|
42026
|
+
judgment: true
|
|
42024
42027
|
},
|
|
42025
42028
|
{
|
|
42026
42029
|
id: "8.7",
|
|
@@ -43414,7 +43417,8 @@ var rgaa_default = {
|
|
|
43414
43417
|
wcag: ["1.1.1", "1.3.1", "1.3.2", "2.4.1", "2.4.3", "3.1.1", "4.1.2"],
|
|
43415
43418
|
appliesTo: {
|
|
43416
43419
|
ruleIds: []
|
|
43417
|
-
}
|
|
43420
|
+
},
|
|
43421
|
+
judgment: true
|
|
43418
43422
|
},
|
|
43419
43423
|
{
|
|
43420
43424
|
id: "13.4",
|
|
@@ -43436,7 +43440,8 @@ var rgaa_default = {
|
|
|
43436
43440
|
wcag: ["1.1.1", "1.3.1", "1.3.2", "2.4.1", "2.4.3", "3.1.1", "4.1.2"],
|
|
43437
43441
|
appliesTo: {
|
|
43438
43442
|
ruleIds: []
|
|
43439
|
-
}
|
|
43443
|
+
},
|
|
43444
|
+
judgment: true
|
|
43440
43445
|
},
|
|
43441
43446
|
{
|
|
43442
43447
|
id: "13.5",
|
|
@@ -45444,6 +45449,10 @@ function title(pack, c2, lang) {
|
|
|
45444
45449
|
function titlePlain(pack, c2, lang) {
|
|
45445
45450
|
return localize(pack, c2.titlePlain, lang);
|
|
45446
45451
|
}
|
|
45452
|
+
function packTestIds(pack, id) {
|
|
45453
|
+
const tests = getCriterion(pack, id)?.tests;
|
|
45454
|
+
return tests ? Object.keys(tests).map((k) => `${id}.${k}`) : [];
|
|
45455
|
+
}
|
|
45447
45456
|
function resolveGlossary(packKey, anchor) {
|
|
45448
45457
|
return packGlossary(packKey)?.[anchor];
|
|
45449
45458
|
}
|
|
@@ -48357,7 +48366,7 @@ function decide(payload, deps = {}) {
|
|
|
48357
48366
|
}
|
|
48358
48367
|
|
|
48359
48368
|
// src/report.ts
|
|
48360
|
-
import { existsSync as existsSync16, mkdirSync as mkdirSync7, writeFileSync as writeFileSync8 } from "fs";
|
|
48369
|
+
import { copyFileSync, existsSync as existsSync16, mkdirSync as mkdirSync7, writeFileSync as writeFileSync8 } from "fs";
|
|
48361
48370
|
import { join as join29, relative as relative3 } from "path";
|
|
48362
48371
|
|
|
48363
48372
|
// src/prd.ts
|
|
@@ -48384,7 +48393,7 @@ function packCriteriaForFinding(pack, finding) {
|
|
|
48384
48393
|
return pack.criteria.filter((pc) => pc.wcag.includes(finding.criteriaId) && (!pc.appliesTo || ruleMatches(finding.ruleId, pc.appliesTo.ruleIds))).map((pc) => pc.id);
|
|
48385
48394
|
}
|
|
48386
48395
|
function packConformancePct(derived) {
|
|
48387
|
-
const c2 = derived.filter((d) => d.status === "C").length;
|
|
48396
|
+
const c2 = derived.filter((d) => d.status === "C" && d.decidedBy !== "agent").length;
|
|
48388
48397
|
const nc = derived.filter((d) => d.status === "NC").length;
|
|
48389
48398
|
return c2 + nc === 0 ? 100 : Math.round(c2 / (c2 + nc) * 100);
|
|
48390
48399
|
}
|
|
@@ -48413,6 +48422,10 @@ function applySecondaryMappings(base, pc, enabled, sources, defaultLocale) {
|
|
|
48413
48422
|
if (added.some((f) => !f.advisory)) return { id: pc.id, theme: pc.theme, status: aggregate2([base.status, "NC"]), findings, scs: base.scs };
|
|
48414
48423
|
return { ...base, findings };
|
|
48415
48424
|
}
|
|
48425
|
+
function judgmentGuard(r, pc) {
|
|
48426
|
+
if (!pc.judgment || r.status !== "C") return r;
|
|
48427
|
+
return { ...r, status: "manual", judgment: true };
|
|
48428
|
+
}
|
|
48416
48429
|
function derivePackResults(audit2, packKey) {
|
|
48417
48430
|
const pack = loadPack(packKey);
|
|
48418
48431
|
const byScId = new Map(audit2.criteria.map((c2) => [c2.id, c2]));
|
|
@@ -48460,7 +48473,7 @@ function derivePackResults(audit2, packKey) {
|
|
|
48460
48473
|
decidedBy: "agent"
|
|
48461
48474
|
};
|
|
48462
48475
|
}
|
|
48463
|
-
const base = deriveBase(pc);
|
|
48476
|
+
const base = judgmentGuard(deriveBase(pc), pc);
|
|
48464
48477
|
return enabledSecondary.length ? applySecondaryMappings(base, pc, enabledSecondary, secondarySources, pack.defaultLocale) : base;
|
|
48465
48478
|
});
|
|
48466
48479
|
}
|
|
@@ -48654,7 +48667,7 @@ function renderAuditorUnit(unit, standard, lang, opts = {}) {
|
|
|
48654
48667
|
const pc = pack.criteria.find((c2) => c2.id === unit.criteriaId);
|
|
48655
48668
|
if (pc) out2.push(`**${v.theme}** : ${pc.theme}. ${themeName(pack, pc.theme, lang) ?? ""}`.trimEnd());
|
|
48656
48669
|
out2.push(`**${v.criterion}** : ${unit.criteriaId} \u2014 ${unit.title}`);
|
|
48657
|
-
const testNums =
|
|
48670
|
+
const testNums = packTestIds(pack, unit.criteriaId);
|
|
48658
48671
|
if (testNums.length) out2.push(`**${v.test}(s)** : ${testNums.join(" \xB7 ")}`);
|
|
48659
48672
|
if (unit.refs.length) out2.push(`**WCAG** : ${unit.refs.map((sc) => `${sc}${scLevel(sc)}`).join(" \xB7 ")}`);
|
|
48660
48673
|
}
|
|
@@ -49066,8 +49079,8 @@ function toRuleOnSection(r, standard, lang) {
|
|
|
49066
49079
|
for (const pc of manual) {
|
|
49067
49080
|
const crit = pack.criteria.find((c2) => c2.id === pc.id);
|
|
49068
49081
|
const title2 = crit ? titlePlain(pack, crit, lang) : pc.id;
|
|
49069
|
-
const tests =
|
|
49070
|
-
out2.push(`- [ ] **${pack.name} ${pc.id}** \u2014 ${title2}${tests.length ? ` \xB7 ${s.tests}: ${tests.map((
|
|
49082
|
+
const tests = packTestIds(pack, pc.id);
|
|
49083
|
+
out2.push(`- [ ] **${pack.name} ${pc.id}** \u2014 ${title2}${tests.length ? ` \xB7 ${s.tests}: ${tests.map((t2) => `\`${t2}\``).join(" ")}` : ""}`);
|
|
49071
49084
|
}
|
|
49072
49085
|
out2.push("");
|
|
49073
49086
|
return out2;
|
|
@@ -49340,6 +49353,8 @@ var L4 = {
|
|
|
49340
49353
|
sev: { bloquant: "Bloquant", majeur: "Majeur", mineur: "Mineur" },
|
|
49341
49354
|
none: "Aucune non-conformit\xE9 d\xE9tect\xE9e par le moteur statique.",
|
|
49342
49355
|
cTitle: "3. Crit\xE8res conformes (C)",
|
|
49356
|
+
cAgentTitle: "Conformes par adjudication de l'agent (jugement, non prouv\xE9 par le moteur)",
|
|
49357
|
+
cAgentNote: "Ces crit\xE8res ont \xE9t\xE9 tranch\xE9s par l'agent IA \xE0 partir des \xE9vidences cit\xE9es, non d\xE9cid\xE9s par le moteur d\xE9terministe. Ils sont gat\xE9s (chaque verdict cite une \xE9vidence r\xE9solvable) mais restent un jugement : ils ne sont pas compt\xE9s dans le taux de r\xE9ussite automatique ci-dessus.",
|
|
49343
49358
|
naTitle: "4. Crit\xE8res non applicables (NA)",
|
|
49344
49359
|
manualTitle: "5. Crit\xE8res \xE0 adjuger (jugement / rendu) \u2014 non d\xE9cid\xE9s par le moteur statique",
|
|
49345
49360
|
manualWarn: "Adjugez-les avec `verify --manual` (l'agent d\xE9cide depuis la source, de fa\xE7on gat\xE9e) ; les crit\xE8res de rendu passent par `scan`. Aucun ne doit \xEAtre marqu\xE9 \xAB conforme \xBB sans justification enregistr\xE9e et gat\xE9e.",
|
|
@@ -49347,6 +49362,7 @@ var L4 = {
|
|
|
49347
49362
|
manualHowTo: "G\xE9n\xE9rez la worklist : `verify --manual --in <audit.json> --standard <pack> --out <dir>`. Chaque item y porte l'\xE9nonc\xE9 complet de ses tests, sa note technique, ses cas particuliers, sa guidance et les termes que le r\xE9f\xE9rentiel d\xE9finit.",
|
|
49348
49363
|
outOfScope: "Hors p\xE9rim\xE8tre moteur \u2014 mapp\xE9 sur des SC hors WCAG 2.2 AA ; v\xE9rification manuelle.",
|
|
49349
49364
|
scopedOut: "Les non-conformit\xE9s WCAG relev\xE9es concernent des \xE9l\xE9ments hors du p\xE9rim\xE8tre de ce crit\xE8re \u2014 \xE0 \xE9valuer s\xE9par\xE9ment.",
|
|
49365
|
+
judgment: "L\u2019\xE9nonc\xE9 du crit\xE8re demande davantage que les CS WCAG auxquels il est rattach\xE9 \u2014 le moteur n\u2019y a pas r\xE9pondu, \xE0 trancher.",
|
|
49350
49366
|
nothing: "Aucun.",
|
|
49351
49367
|
dedup: "D\xE9dup",
|
|
49352
49368
|
canonical: "fichier(s) canonique(s) audit\xE9(s)",
|
|
@@ -49392,6 +49408,8 @@ var L4 = {
|
|
|
49392
49408
|
sev: { bloquant: "Blocking", majeur: "Major", mineur: "Minor" },
|
|
49393
49409
|
none: "No non-conformity detected by the static engine.",
|
|
49394
49410
|
cTitle: "3. Conforming criteria (C)",
|
|
49411
|
+
cAgentTitle: "Conforming by agent adjudication (judgement, not proven by the engine)",
|
|
49412
|
+
cAgentNote: "These criteria were ruled on by the AI agent from the evidence it cited, not decided by the deterministic engine. They are gated (every verdict cites resolvable evidence) but remain a judgement: they are not counted in the automatic pass rate above.",
|
|
49395
49413
|
naTitle: "4. Not-applicable criteria (NA)",
|
|
49396
49414
|
manualTitle: "5. Criteria to adjudicate (judgment / rendering) \u2014 not decided by the static engine",
|
|
49397
49415
|
manualWarn: "Adjudicate these with `verify --manual` (the agent decides from source, gated); rendering criteria go to `scan`. None may be marked \u201Cconforming\u201D without a recorded, gated justification.",
|
|
@@ -49399,6 +49417,7 @@ var L4 = {
|
|
|
49399
49417
|
manualHowTo: "Generate the worklist: `verify --manual --in <audit.json> --standard <pack> --out <dir>`. Each item carries the full wording of its tests, its technical note, its particular cases, its guidance and the terms the standard defines.",
|
|
49400
49418
|
outOfScope: "Out of engine scope \u2014 mapped to SCs outside WCAG 2.2 AA; manual verification.",
|
|
49401
49419
|
scopedOut: "The WCAG failures found concern elements outside this criterion's scope \u2014 assess separately.",
|
|
49420
|
+
judgment: "The criterion asks more than the WCAG SCs it maps to \u2014 the engine did not answer it; rule on it.",
|
|
49402
49421
|
nothing: "None.",
|
|
49403
49422
|
dedup: "Dedup",
|
|
49404
49423
|
canonical: "canonical file(s) audited",
|
|
@@ -49507,11 +49526,18 @@ function render(r, lang, opts) {
|
|
|
49507
49526
|
const notes = pageScope.find((x) => x.id === pg.id)?.notes;
|
|
49508
49527
|
if (notes) out2.push(`- _${notes}_`);
|
|
49509
49528
|
const shot = join29(PAGES_DIR, pg.id, "screen.png");
|
|
49510
|
-
if (existsSync16(shot))
|
|
49511
|
-
|
|
49512
|
-
|
|
49513
|
-
|
|
49514
|
-
|
|
49529
|
+
if (existsSync16(shot)) {
|
|
49530
|
+
let href = relative3(opts.outDir ?? ".", shot).split("\\").join("/");
|
|
49531
|
+
if (opts.outDir) {
|
|
49532
|
+
try {
|
|
49533
|
+
mkdirSync7(join29(opts.outDir, "assets"), { recursive: true });
|
|
49534
|
+
copyFileSync(shot, join29(opts.outDir, "assets", `${pg.id}.png`));
|
|
49535
|
+
href = `./assets/${pg.id}.png`;
|
|
49536
|
+
} catch {
|
|
49537
|
+
}
|
|
49538
|
+
}
|
|
49539
|
+
out2.push("", ``);
|
|
49540
|
+
}
|
|
49515
49541
|
for (const f of nc.slice(0, 30)) {
|
|
49516
49542
|
const crits = pack ? packCriteriaForFinding(pack, f) : [];
|
|
49517
49543
|
const label = crits.length ? crits.join(", ") : f.criteriaId;
|
|
@@ -49522,7 +49548,16 @@ function render(r, lang, opts) {
|
|
|
49522
49548
|
}
|
|
49523
49549
|
out2.push(`## ${s.cTitle}`, "");
|
|
49524
49550
|
const conform = rows.filter((x) => x.status === "C");
|
|
49525
|
-
|
|
49551
|
+
const byEngine = conform.filter((x) => x.decidedBy !== "agent");
|
|
49552
|
+
const byAgent = conform.filter((x) => x.decidedBy === "agent");
|
|
49553
|
+
if (!conform.length) out2.push(s.nothing, "");
|
|
49554
|
+
else {
|
|
49555
|
+
if (byEngine.length) out2.push(...byEngine.map((x) => `- ${x.label}`), "");
|
|
49556
|
+
if (byAgent.length) {
|
|
49557
|
+
out2.push(`### ${s.cAgentTitle}`, "", `> ${s.cAgentNote}`, "");
|
|
49558
|
+
out2.push(...byAgent.map((x) => `- ${x.label}${x.justification ? ` \u2014 _${x.justification}_` : ""}`), "");
|
|
49559
|
+
}
|
|
49560
|
+
}
|
|
49526
49561
|
out2.push(`## ${s.naTitle}`, "");
|
|
49527
49562
|
const na = rows.filter((x) => x.status === "NA");
|
|
49528
49563
|
out2.push(na.length ? na.map((x) => `- ${x.label}${x.justification ? ` \u2014 _${x.justification}_` : ""}`).join("\n") : s.nothing, "");
|
|
@@ -49532,8 +49567,8 @@ function render(r, lang, opts) {
|
|
|
49532
49567
|
else {
|
|
49533
49568
|
const pack5 = isCore(opts.standard) ? void 0 : loadPack(opts.standard);
|
|
49534
49569
|
for (const x of manual) {
|
|
49535
|
-
const tests = pack5 ?
|
|
49536
|
-
const testRef = tests.length ? ` \u2014 ${s.testsToRule}: ${tests.map((
|
|
49570
|
+
const tests = pack5 ? packTestIds(pack5, x.id) : [];
|
|
49571
|
+
const testRef = tests.length ? ` \u2014 ${s.testsToRule}: ${tests.map((t2) => `\`${t2}\``).join(" \xB7 ")}` : "";
|
|
49537
49572
|
out2.push(`- ${x.label}${x.justification ? ` \u2014 _${x.justification}_` : ""}${testRef}`);
|
|
49538
49573
|
}
|
|
49539
49574
|
out2.push("", `> ${s.manualHowTo}`, "");
|
|
@@ -49545,7 +49580,14 @@ function renderReport(r, lang = "en", outDir) {
|
|
|
49545
49580
|
const byGuideline = /* @__PURE__ */ new Map();
|
|
49546
49581
|
for (const c2 of r.criteria) {
|
|
49547
49582
|
const title2 = scTitle(c2.id, lang);
|
|
49548
|
-
const row = {
|
|
49583
|
+
const row = {
|
|
49584
|
+
id: c2.id,
|
|
49585
|
+
label: title2 ? `${c2.id} \u2014 ${title2}` : c2.id,
|
|
49586
|
+
status: c2.status,
|
|
49587
|
+
findings: c2.findings,
|
|
49588
|
+
justification: c2.justification,
|
|
49589
|
+
decidedBy: c2.decidedBy
|
|
49590
|
+
};
|
|
49549
49591
|
(byGuideline.get(c2.guideline) ?? byGuideline.set(c2.guideline, []).get(c2.guideline)).push(row);
|
|
49550
49592
|
}
|
|
49551
49593
|
const groups = r.guidelines.map((g) => ({ key: g.key, title: guidelineTitle(g.key, lang) ?? g.title, rows: byGuideline.get(g.key) ?? [] }));
|
|
@@ -49564,9 +49606,10 @@ function renderPackReport(r, pack, lang = "en", outDir) {
|
|
|
49564
49606
|
label: `${pack.name} ${pr.id} \u2014 ${title(pack, pc, lang)}`,
|
|
49565
49607
|
status: pr.status,
|
|
49566
49608
|
findings: pr.findings,
|
|
49609
|
+
...pr.decidedBy ? { decidedBy: pr.decidedBy } : {},
|
|
49567
49610
|
// outOfScope / scopedOut criteria are "manual" (not NA) with their own dedicated
|
|
49568
49611
|
// justification — never mixed with the ordinary NA reason (see the manual section above).
|
|
49569
|
-
...pr.outOfScope ? { justification: s.outOfScope } : pr.scopedOut ? { justification: s.scopedOut } : pr.status === "NA" ? { justification: naReason } : {}
|
|
49612
|
+
...pr.outOfScope ? { justification: s.outOfScope } : pr.scopedOut ? { justification: s.scopedOut } : pr.judgment ? { justification: s.judgment } : pr.status === "NA" ? { justification: naReason } : {}
|
|
49570
49613
|
};
|
|
49571
49614
|
(byTheme.get(pr.theme) ?? byTheme.set(pr.theme, []).get(pr.theme)).push(row);
|
|
49572
49615
|
}
|
|
@@ -49578,7 +49621,11 @@ function renderPackReport(r, pack, lang = "en", outDir) {
|
|
|
49578
49621
|
derivedOf: std,
|
|
49579
49622
|
standard: pack.key,
|
|
49580
49623
|
partialAudit: untestedNeedsRendering(r),
|
|
49581
|
-
headerRatePct: packConformancePct(derived)
|
|
49624
|
+
headerRatePct: packConformancePct(derived),
|
|
49625
|
+
// Forwarded, unlike before: without it the per-page screenshots resolved against the
|
|
49626
|
+
// CWD instead of the report's own directory, so a pack report written to `audits/`
|
|
49627
|
+
// carried links that only worked when read from the repo root.
|
|
49628
|
+
outDir
|
|
49582
49629
|
});
|
|
49583
49630
|
}
|
|
49584
49631
|
function writeReport(r, opts) {
|
|
@@ -50462,9 +50509,12 @@ var L6 = {
|
|
|
50462
50509
|
viewport: "Fen\xEAtre",
|
|
50463
50510
|
producer: "Producteur",
|
|
50464
50511
|
auth: "Authentification requise",
|
|
50465
|
-
rate: "Taux de
|
|
50512
|
+
rate: "Taux de r\xE9ussite automatique (v\xE9rifications statiques)",
|
|
50466
50513
|
rateNote: "sous-ensemble d\xE9cidable : C \xF7 (C + NC)",
|
|
50467
50514
|
tally: (c2, nc, na, m) => `${c2} conforme(s) \xB7 ${nc} non conforme(s) \xB7 ${na} non applicable(s) \xB7 ${m} \xE0 \xE9valuer`,
|
|
50515
|
+
coverage: (decided, total) => `Couverture : ${decided}/${total} crit\xE8re(s) \xE9valu\xE9(s) \u2014 le taux ci-dessus ne porte que sur eux, et ne dit rien des ${total - decided} autres.`,
|
|
50516
|
+
tests: "Tests",
|
|
50517
|
+
agentMark: "`C*` : conformit\xE9 tranch\xE9e par l'agent IA \xE0 partir des \xE9vidences cit\xE9es (gat\xE9), et non prouv\xE9e par le moteur d\xE9terministe.",
|
|
50468
50518
|
screenshotAlt: (n) => `Capture d'\xE9cran de la page ${n}`,
|
|
50469
50519
|
noScreenshot: "Aucune capture d'\xE9cran pour cette page (le producteur n'en a pas fourni) \u2014 le tier pixel est donc inactif ici.",
|
|
50470
50520
|
gridTitle: "Grille des crit\xE8res",
|
|
@@ -50499,9 +50549,12 @@ var L6 = {
|
|
|
50499
50549
|
viewport: "Viewport",
|
|
50500
50550
|
producer: "Producer",
|
|
50501
50551
|
auth: "Authentication required",
|
|
50502
|
-
rate: "
|
|
50552
|
+
rate: "Automatic static-check pass rate",
|
|
50503
50553
|
rateNote: "decidable subset: C \xF7 (C + NC)",
|
|
50504
50554
|
tally: (c2, nc, na, m) => `${c2} conforming \xB7 ${nc} non-conforming \xB7 ${na} not applicable \xB7 ${m} to assess`,
|
|
50555
|
+
coverage: (decided, total) => `Coverage: ${decided}/${total} criteria assessed \u2014 the rate above covers only those, and says nothing about the other ${total - decided}.`,
|
|
50556
|
+
tests: "Tests",
|
|
50557
|
+
agentMark: "`C*`: conformity ruled by the AI agent from the evidence it cited (gated), not proven by the deterministic engine.",
|
|
50505
50558
|
screenshotAlt: (n) => `Screenshot of the ${n} page`,
|
|
50506
50559
|
noScreenshot: "No screenshot for this page (the producer supplied none) \u2014 the pixel tier is therefore inactive here.",
|
|
50507
50560
|
gridTitle: "Criteria grid",
|
|
@@ -50524,15 +50577,24 @@ var L6 = {
|
|
|
50524
50577
|
};
|
|
50525
50578
|
function rowsFor(result, page, standard, lang) {
|
|
50526
50579
|
if (isCore(standard)) {
|
|
50527
|
-
return [...page.criteria].sort((a, b) => compareSC(a.id, b.id)).map((c2) => ({
|
|
50580
|
+
return [...page.criteria].sort((a, b) => compareSC(a.id, b.id)).map((c2) => ({
|
|
50581
|
+
id: c2.id,
|
|
50582
|
+
label: `${c2.id} ${scTitle(c2.id, lang) ?? ""}`.trim(),
|
|
50583
|
+
group: c2.guideline,
|
|
50584
|
+
status: c2.status,
|
|
50585
|
+
tests: [],
|
|
50586
|
+
decidedBy: c2.decidedBy
|
|
50587
|
+
}));
|
|
50528
50588
|
}
|
|
50529
50589
|
const pack = loadPack(standard);
|
|
50530
|
-
const byId2 = new Map(derivePackResults(pageView(result, page), standard).map((r) => [r.id, r
|
|
50590
|
+
const byId2 = new Map(derivePackResults(pageView(result, page), standard).map((r) => [r.id, r]));
|
|
50531
50591
|
return pack.criteria.map((pc) => ({
|
|
50532
50592
|
id: pc.id,
|
|
50533
50593
|
label: `${pc.id} \u2014 ${titlePlain(pack, pc, lang)}`,
|
|
50534
50594
|
group: `${pc.theme}. ${themeName(pack, pc.theme, lang) ?? ""}`.trim(),
|
|
50535
|
-
status: byId2.get(pc.id) ?? "manual"
|
|
50595
|
+
status: byId2.get(pc.id)?.status ?? "manual",
|
|
50596
|
+
tests: packTestIds(pack, pc.id),
|
|
50597
|
+
decidedBy: byId2.get(pc.id)?.decidedBy
|
|
50536
50598
|
}));
|
|
50537
50599
|
}
|
|
50538
50600
|
function tally(rows) {
|
|
@@ -50562,22 +50624,28 @@ function renderPageReport(result, page, opts = {}) {
|
|
|
50562
50624
|
const rows = rowsFor(result, page, standard, lang);
|
|
50563
50625
|
const t2 = tally(rows);
|
|
50564
50626
|
out2.push(`- **${s.rate}** : **${ratePct(rows)} %** _(${s.rateNote})_`);
|
|
50565
|
-
out2.push(`- ${s.tally(t2.c, t2.nc, t2.na, t2.manual)}
|
|
50627
|
+
out2.push(`- ${s.tally(t2.c, t2.nc, t2.na, t2.manual)}`);
|
|
50628
|
+
out2.push(`- ${s.coverage(t2.c + t2.nc, rows.length)}`, "");
|
|
50566
50629
|
if (page.basis !== "snapshot") out2.push(`> \u26A0\uFE0F ${s.sourceWarn}`, "");
|
|
50567
50630
|
const shot = opts.screenshots?.get(page.id);
|
|
50568
50631
|
if (shot) out2.push(``, "");
|
|
50569
50632
|
else out2.push(`_${s.noScreenshot}_`, "");
|
|
50633
|
+
const withTests = rows.some((r) => r.tests.length);
|
|
50570
50634
|
out2.push(`${h}# ${s.gridTitle}`, "", `> ${s.gridNote}`, "");
|
|
50571
|
-
out2.push(`| ${s.criterion} | ${s.status}
|
|
50635
|
+
out2.push(withTests ? `| ${s.criterion} | ${s.tests} | ${s.status} |` : `| ${s.criterion} | ${s.status} |`);
|
|
50636
|
+
out2.push(withTests ? "| --- | --- | --- |" : "| --- | --- |");
|
|
50572
50637
|
let group = "";
|
|
50573
50638
|
for (const row of rows) {
|
|
50574
50639
|
if (row.group !== group) {
|
|
50575
50640
|
group = row.group;
|
|
50576
|
-
out2.push(`| **${group}** | |`);
|
|
50641
|
+
out2.push(withTests ? `| **${group}** | | |` : `| **${group}** | |`);
|
|
50577
50642
|
}
|
|
50578
|
-
|
|
50643
|
+
const mark = row.decidedBy === "agent" && row.status === "C" ? `${MARK2[row.status]}*` : MARK2[row.status];
|
|
50644
|
+
if (withTests) out2.push(`| ${row.label} | ${row.tests.map((t3) => `\`${t3}\``).join(" ")} | ${mark} |`);
|
|
50645
|
+
else out2.push(`| ${row.label} | ${mark} |`);
|
|
50579
50646
|
}
|
|
50580
50647
|
out2.push("", `> ${s.manualWarn}`, "");
|
|
50648
|
+
if (rows.some((r) => r.decidedBy === "agent" && r.status === "C")) out2.push(`> ${s.agentMark}`, "");
|
|
50581
50649
|
const units = prdUnits(pageView(result, page), standard, lang);
|
|
50582
50650
|
const ncUnits = units.filter((u) => !u.advisory);
|
|
50583
50651
|
const advUnits = units.filter((u) => u.advisory);
|
|
@@ -51786,12 +51854,23 @@ function applyAdjudication(audit2, adj, opts = {}) {
|
|
|
51786
51854
|
const issues = [];
|
|
51787
51855
|
const byId2 = new Map(adj.items.map((it) => [it.criteriaId, it]));
|
|
51788
51856
|
const packMode = !isCore(adj.standard);
|
|
51857
|
+
const open = /* @__PURE__ */ new Set();
|
|
51789
51858
|
if (packMode) {
|
|
51790
51859
|
for (const pc of derivePackResults(audit2, adj.standard)) {
|
|
51791
|
-
if (pc.status
|
|
51860
|
+
if (pc.status !== "manual") continue;
|
|
51861
|
+
open.add(pc.id);
|
|
51862
|
+
if (!byId2.has(pc.id)) issues.push(`criterion ${pc.id}: missing from the adjudication (coverage gap)`);
|
|
51792
51863
|
}
|
|
51793
51864
|
} else {
|
|
51794
|
-
for (const r of audit2.residualRisks)
|
|
51865
|
+
for (const r of audit2.residualRisks) {
|
|
51866
|
+
open.add(r.criteriaId);
|
|
51867
|
+
if (!byId2.has(r.criteriaId)) issues.push(`criterion ${r.criteriaId}: missing from the adjudication (coverage gap)`);
|
|
51868
|
+
}
|
|
51869
|
+
}
|
|
51870
|
+
for (const it of adj.items) {
|
|
51871
|
+
if (!open.has(it.criteriaId)) {
|
|
51872
|
+
issues.push(`criterion ${it.criteriaId}: not open for adjudication \u2014 the engine already decided it, or it is not part of ${adj.standard}`);
|
|
51873
|
+
}
|
|
51795
51874
|
}
|
|
51796
51875
|
const groundInputs = [];
|
|
51797
51876
|
for (const it of adj.items) {
|
|
@@ -51800,6 +51879,26 @@ function applyAdjudication(audit2, adj, opts = {}) {
|
|
|
51800
51879
|
issues.push(`criterion ${it.criteriaId}: unadjudicated (verdict is null)`);
|
|
51801
51880
|
} else if (v === "C" || v === "NA") {
|
|
51802
51881
|
if (!it.justification || !it.justification.trim()) issues.push(`criterion ${it.criteriaId}: a ${v} verdict requires a justification`);
|
|
51882
|
+
const cites = it.citations ?? [];
|
|
51883
|
+
if (it.evidence.length === 0) {
|
|
51884
|
+
if (v === "C") {
|
|
51885
|
+
issues.push(
|
|
51886
|
+
`criterion ${it.criteriaId}: a C verdict needs evidence to cite, and none was harvested for this criterion \u2014 record "manual" (reason "undecidable"), or "NA" if nothing in scope is concerned`
|
|
51887
|
+
);
|
|
51888
|
+
}
|
|
51889
|
+
} else if (cites.length === 0) {
|
|
51890
|
+
issues.push(
|
|
51891
|
+
`criterion ${it.criteriaId}: a ${v} verdict must cite at least one of the ${it.evidence.length} evidence item(s) it was shown (citations: [{file, line, \u2026}])`
|
|
51892
|
+
);
|
|
51893
|
+
} else {
|
|
51894
|
+
const anchors = new Set(it.evidence.map((e) => `${e.file}:${e.line}`));
|
|
51895
|
+
for (const c2 of cites) {
|
|
51896
|
+
if (!anchors.has(`${c2.file}:${c2.line}`)) {
|
|
51897
|
+
issues.push(`criterion ${it.criteriaId}: citation ${c2.file}:${c2.line} is not among this criterion's harvested evidence (fabricated?)`);
|
|
51898
|
+
}
|
|
51899
|
+
groundInputs.push({ file: c2.file, line: c2.line, selector: c2.selector, snippet: c2.snippet });
|
|
51900
|
+
}
|
|
51901
|
+
}
|
|
51803
51902
|
} else if (v === "NC") {
|
|
51804
51903
|
if (!it.findings || it.findings.length === 0) issues.push(`criterion ${it.criteriaId}: an NC verdict requires at least one groundable finding`);
|
|
51805
51904
|
for (const f of it.findings ?? []) {
|
|
@@ -51921,7 +52020,7 @@ function recomputeTallies(a) {
|
|
|
51921
52020
|
g.na = inG.filter((c2) => c2.status === "NA").length;
|
|
51922
52021
|
g.manual = inG.filter((c2) => c2.status === "manual").length;
|
|
51923
52022
|
}
|
|
51924
|
-
const decided = a.criteria.filter((c2) => c2.status === "
|
|
52023
|
+
const decided = a.criteria.filter((c2) => c2.status === "NC" || c2.status === "C" && c2.decidedBy !== "agent");
|
|
51925
52024
|
const conform = decided.filter((c2) => c2.status === "C").length;
|
|
51926
52025
|
a.conformancePct = decided.length === 0 ? 100 : Math.round(conform / decided.length * 100);
|
|
51927
52026
|
}
|
|
@@ -51932,13 +52031,13 @@ var T2 = {
|
|
|
51932
52031
|
title: "# Adjudication des crit\xE8res \xE0 \xE9valuer (ultra11y)",
|
|
51933
52032
|
intro: "Pour CHAQUE crit\xE8re, lisez les \xE9vidences ci-dessous (extraites de la source audit\xE9e) et attribuez un verdict dans `ADJUDICATE.todo.json` (champ `verdict`) :",
|
|
51934
52033
|
verdicts: [
|
|
51935
|
-
"- `C` \u2014 conforme (renseignez `justification`) ;",
|
|
52034
|
+
"- `C` \u2014 conforme (renseignez `justification` ET `citations[]` : les \xE9vidences que vous avez lev\xE9es, `file`/`line` recopi\xE9s depuis la liste du crit\xE8re) ;",
|
|
51936
52035
|
"- `NC` \u2014 non conforme (ajoutez au moins un `findings[]` : file/line/message, avec un `snippet` groundable ET un `normativeRef` citant le test pr\xE9cis \xE9chou\xE9) ;",
|
|
51937
|
-
"- `NA` \u2014 non applicable (renseignez `justification`) ;",
|
|
52036
|
+
"- `NA` \u2014 non applicable (renseignez `justification` ; si des \xE9vidences sont pr\xE9sent\xE9es, citez-les aussi dans `citations[]` pour dire lesquelles sortent du p\xE9rim\xE8tre) ;",
|
|
51938
52037
|
"- `manual` \u2014 ind\xE9cidable statiquement (renseignez `reason` : `needs-rendered-dom` \u2192 `scan`, ou `undecidable`)."
|
|
51939
52038
|
],
|
|
51940
|
-
rule: "> Ne signalez une NC que si un test pr\xE9cis du r\xE9f\xE9rentiel actif \xE9choue \u2014 citez-le (`normativeRef`). Une bonne pratique sans test normatif est une recommandation (`recommendations[]`, non normative). Une simple pr\xE9occupation UX n'est ni l'un ni l'autre.",
|
|
51941
|
-
then: "Puis : `ultra11y verify --apply ADJUDICATE.todo.json --in <audit.json> --out <dir>` (\xE9choue si un verdict manque
|
|
52039
|
+
rule: "> Ne signalez une NC que si un test pr\xE9cis du r\xE9f\xE9rentiel actif \xE9choue \u2014 citez-le (`normativeRef`). Une bonne pratique sans test normatif est une recommandation (`recommendations[]`, non normative). Une simple pr\xE9occupation UX n'est ni l'un ni l'autre.\n>\n> Sym\xE9triquement, un `C` se cite comme une NC : il faut nommer dans `citations[]` les \xE9vidences lev\xE9es. **Un crit\xE8re pr\xE9sent\xE9 sans aucune \xE9vidence ne peut pas \xEAtre `C`** \u2014 c'est `manual` (`undecidable`), ou `NA` si rien n'est concern\xE9.",
|
|
52040
|
+
then: "Puis : `ultra11y verify --apply ADJUDICATE.todo.json --in <audit.json> --out <dir>` (\xE9choue si un verdict manque sa justification, ses citations, son finding ou sa raison).",
|
|
51942
52041
|
evidence: "\xC9vidences",
|
|
51943
52042
|
none: "(aucune \xE9vidence automatique \u2014 d\xE9cidez depuis la source, ou laissez `manual` avec une raison)",
|
|
51944
52043
|
questions: "\xC0 v\xE9rifier manuellement",
|
|
@@ -51955,13 +52054,13 @@ var T2 = {
|
|
|
51955
52054
|
title: "# Criteria adjudication (ultra11y)",
|
|
51956
52055
|
intro: "For EACH criterion, read the evidence below (harvested from the audited source) and set a verdict in `ADJUDICATE.todo.json` (field `verdict`):",
|
|
51957
52056
|
verdicts: [
|
|
51958
|
-
"- `C` \u2014 conformant (fill `justification`);",
|
|
52057
|
+
"- `C` \u2014 conformant (fill `justification` AND `citations[]`: the evidence you cleared, `file`/`line` copied from the criterion's own list);",
|
|
51959
52058
|
"- `NC` \u2014 non-conformant (add at least one `findings[]`: file/line/message, with a groundable `snippet` AND a `normativeRef` citing the precise failed test);",
|
|
51960
|
-
"- `NA` \u2014 not applicable (fill `justification`);",
|
|
52059
|
+
"- `NA` \u2014 not applicable (fill `justification`; when evidence is presented, cite it too in `citations[]` to say which items are out of scope);",
|
|
51961
52060
|
"- `manual` \u2014 not statically decidable (fill `reason`: `needs-rendered-dom` \u2192 `scan`, or `undecidable`)."
|
|
51962
52061
|
],
|
|
51963
|
-
rule: "> Report NC only if a precise test of the active standard fails \u2014 cite it (`normativeRef`). A good practice without a normative test is a recommendation (`recommendations[]`, non-normative). A purely UX concern is neither.",
|
|
51964
|
-
then: "Then: `ultra11y verify --apply ADJUDICATE.todo.json --in <audit.json> --out <dir>` (fails if any verdict lacks its justification
|
|
52062
|
+
rule: "> Report NC only if a precise test of the active standard fails \u2014 cite it (`normativeRef`). A good practice without a normative test is a recommendation (`recommendations[]`, non-normative). A purely UX concern is neither.\n>\n> A `C` is cited the same way an NC is: name the evidence you cleared in `citations[]`. **A criterion presented with no evidence at all cannot be `C`** \u2014 record `manual` (`undecidable`), or `NA` if nothing in scope is concerned.",
|
|
52063
|
+
then: "Then: `ultra11y verify --apply ADJUDICATE.todo.json --in <audit.json> --out <dir>` (fails if any verdict lacks its justification, citations, finding or reason).",
|
|
51965
52064
|
evidence: "Evidence",
|
|
51966
52065
|
none: "(no automatic evidence \u2014 decide from source, or leave `manual` with a reason)",
|
|
51967
52066
|
questions: "To verify manually",
|
|
@@ -52135,6 +52234,20 @@ var VERDICT_TOOL = {
|
|
|
52135
52234
|
required: ["file", "line", "message", "normativeRef"]
|
|
52136
52235
|
}
|
|
52137
52236
|
},
|
|
52237
|
+
citations: {
|
|
52238
|
+
type: "array",
|
|
52239
|
+
description: "Required for C and NA when evidence was presented: the evidence items you cleared (or ruled out of scope). Copy `file`, `line` and `snippet` VERBATIM from the evidence list of this criterion \u2014 a citation that is not among them, or that no longer matches the source, is rejected.",
|
|
52240
|
+
items: {
|
|
52241
|
+
type: "object",
|
|
52242
|
+
properties: {
|
|
52243
|
+
file: { type: "string" },
|
|
52244
|
+
line: { type: "number" },
|
|
52245
|
+
selector: { type: "string" },
|
|
52246
|
+
snippet: { type: "string" }
|
|
52247
|
+
},
|
|
52248
|
+
required: ["file", "line"]
|
|
52249
|
+
}
|
|
52250
|
+
},
|
|
52138
52251
|
recommendations: {
|
|
52139
52252
|
type: "array",
|
|
52140
52253
|
description: "Non-normative good practices. They never change a status.",
|
|
@@ -52163,7 +52276,7 @@ var SYSTEM = `You are an accessibility auditor ruling on the criteria a static e
|
|
|
52163
52276
|
Rules, in order of importance:
|
|
52164
52277
|
1. NEVER assert conformity you did not verify. "manual" with a reason is always available and is a correct answer.
|
|
52165
52278
|
2. An NC must cite a real file:line taken from the evidence you were given, and the criterion's OWN numbered test as normativeRef. A citation that does not resolve against the real source is rejected downstream and wastes the whole batch.
|
|
52166
|
-
3. C and NA require a justification that says what you saw,
|
|
52279
|
+
3. C and NA require a justification that says what you saw, AND a "citations" array naming the evidence items you cleared \u2014 file and line copied verbatim from the evidence presented for that criterion. A criterion presented with NO evidence cannot be C: record "manual" (reason "undecidable"), or NA if nothing in scope is concerned.
|
|
52167
52280
|
4. A criterion that needs a rendered page (computed contrast, visible focus, zoom, reflow) and was given only source evidence is "manual" with reason "needs-rendered-dom".
|
|
52168
52281
|
5. Rule only on the criteria presented. Never introduce another.`;
|
|
52169
52282
|
var sleep = (ms) => ms <= 0 ? Promise.resolve() : new Promise((r) => setTimeout(r, ms));
|
|
@@ -52247,6 +52360,8 @@ function applyRawVerdicts(items, verdicts) {
|
|
|
52247
52360
|
item.justification = v.justification ?? "";
|
|
52248
52361
|
item.reason = v.verdict === "manual" ? v.reason ?? null : null;
|
|
52249
52362
|
item.findings = v.verdict === "NC" ? v.findings ?? [] : [];
|
|
52363
|
+
if (v.verdict === "C" || v.verdict === "NA") item.citations = v.citations ?? [];
|
|
52364
|
+
else delete item.citations;
|
|
52250
52365
|
if (v.recommendations?.length) item.recommendations = v.recommendations;
|
|
52251
52366
|
filled++;
|
|
52252
52367
|
}
|
|
@@ -54213,7 +54328,7 @@ function writeCi(root, enginePath, failOn) {
|
|
|
54213
54328
|
}
|
|
54214
54329
|
|
|
54215
54330
|
// src/install/json-edit.ts
|
|
54216
|
-
import { copyFileSync, existsSync as existsSync22, mkdirSync as mkdirSync11, readFileSync as readFileSync17, renameSync as renameSync2, rmSync as rmSync4, writeFileSync as writeFileSync14 } from "fs";
|
|
54331
|
+
import { copyFileSync as copyFileSync2, existsSync as existsSync22, mkdirSync as mkdirSync11, readFileSync as readFileSync17, renameSync as renameSync2, rmSync as rmSync4, writeFileSync as writeFileSync14 } from "fs";
|
|
54217
54332
|
import { dirname as dirname10 } from "path";
|
|
54218
54333
|
var SettingsParseError = class extends Error {
|
|
54219
54334
|
constructor(path, cause) {
|
|
@@ -54232,7 +54347,7 @@ function writeTextWithBackup(path, content, marker = "ultra11y") {
|
|
|
54232
54347
|
let backup;
|
|
54233
54348
|
if (existsSync22(path)) {
|
|
54234
54349
|
backup = `${path}.${marker}-backup-${stamp()}`;
|
|
54235
|
-
|
|
54350
|
+
copyFileSync2(path, backup);
|
|
54236
54351
|
}
|
|
54237
54352
|
const tmp = `${path}.${process.pid}.tmp`;
|
|
54238
54353
|
try {
|
|
@@ -54405,7 +54520,7 @@ import { existsSync as existsSync25, readFileSync as readFileSync20 } from "fs";
|
|
|
54405
54520
|
import { join as join37 } from "path";
|
|
54406
54521
|
|
|
54407
54522
|
// src/install/paths.ts
|
|
54408
|
-
import { copyFileSync as
|
|
54523
|
+
import { copyFileSync as copyFileSync3, cpSync, existsSync as existsSync24, mkdirSync as mkdirSync12, readFileSync as readFileSync19, realpathSync as realpathSync3, statSync as statSync10 } from "fs";
|
|
54409
54524
|
import { homedir as homedir2 } from "os";
|
|
54410
54525
|
import { dirname as dirname11, join as join36 } from "path";
|
|
54411
54526
|
function codexHome() {
|
|
@@ -54435,7 +54550,7 @@ function installedCliCommand() {
|
|
|
54435
54550
|
const pin = pinnedEnginePath();
|
|
54436
54551
|
if (source) {
|
|
54437
54552
|
mkdirSync12(join36(pin, ".."), { recursive: true });
|
|
54438
|
-
|
|
54553
|
+
copyFileSync3(source, pin);
|
|
54439
54554
|
}
|
|
54440
54555
|
return `node ${JSON.stringify(pin)}`;
|
|
54441
54556
|
}
|
|
@@ -58351,27 +58466,39 @@ async function cmdPages(p) {
|
|
|
58351
58466
|
const derived = derivePages(result, scope);
|
|
58352
58467
|
const split = p.flags.split === "page";
|
|
58353
58468
|
const outDir = typeof p.flags.out === "string" && p.flags.out ? p.flags.out : void 0;
|
|
58354
|
-
const
|
|
58469
|
+
const shotOf = (id) => join45(PAGES_DIR, id, "screen.png");
|
|
58470
|
+
const shotsRelative = (fileDir) => {
|
|
58471
|
+
const m = /* @__PURE__ */ new Map();
|
|
58472
|
+
for (const pg of derived) {
|
|
58473
|
+
if (existsSync31(shotOf(pg.id))) m.set(pg.id, relative5(fileDir, shotOf(pg.id)).split("\\").join("/"));
|
|
58474
|
+
}
|
|
58475
|
+
return m;
|
|
58476
|
+
};
|
|
58477
|
+
const shotsCopiedInto = (dir) => {
|
|
58355
58478
|
const m = /* @__PURE__ */ new Map();
|
|
58479
|
+
const assets = join45(dir, "assets");
|
|
58356
58480
|
for (const pg of derived) {
|
|
58357
|
-
const
|
|
58358
|
-
if (existsSync31(
|
|
58481
|
+
const src = shotOf(pg.id);
|
|
58482
|
+
if (!existsSync31(src)) continue;
|
|
58483
|
+
mkdirSync15(assets, { recursive: true });
|
|
58484
|
+
copyFileSync4(src, join45(assets, `${pg.id}.png`));
|
|
58485
|
+
m.set(pg.id, `./assets/${pg.id}.png`);
|
|
58359
58486
|
}
|
|
58360
58487
|
return m;
|
|
58361
58488
|
};
|
|
58362
58489
|
if (!outDir) {
|
|
58363
|
-
console.log(renderPagesDocument(result, derived, { standard, lang, screenshots:
|
|
58490
|
+
console.log(renderPagesDocument(result, derived, { standard, lang, screenshots: shotsRelative(".") }));
|
|
58364
58491
|
return 0;
|
|
58365
58492
|
}
|
|
58366
58493
|
mkdirSync15(outDir, { recursive: true });
|
|
58367
58494
|
if (!split) {
|
|
58368
58495
|
const file = join45(outDir, `pages-${result.date}.md`);
|
|
58369
|
-
writeFileSync17(file, `${renderPagesDocument(result, derived, { standard, lang, screenshots:
|
|
58496
|
+
writeFileSync17(file, `${renderPagesDocument(result, derived, { standard, lang, screenshots: shotsCopiedInto(outDir) })}
|
|
58370
58497
|
`);
|
|
58371
58498
|
console.log(file);
|
|
58372
58499
|
return 0;
|
|
58373
58500
|
}
|
|
58374
|
-
const shots =
|
|
58501
|
+
const shots = shotsCopiedInto(outDir);
|
|
58375
58502
|
const sheet = (id) => `page-${id}.md`;
|
|
58376
58503
|
const hrefs = new Map(derived.map((pg) => [pg.id, `./${sheet(pg.id)}`]));
|
|
58377
58504
|
for (const pg of derived) {
|
|
@@ -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-
|
|
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-12 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,
|
|
@@ -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: 2.31.
|
|
8
|
+
version: 2.31.3
|
|
9
9
|
---
|
|
10
10
|
|
|
11
11
|
# review-a11y — accessibility review of changed code
|