ultra11y 5.36.0 → 5.36.2

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": "5.36.0",
13
+ "version": "5.36.2",
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": "5.36.0",
4
+ "version": "5.36.2",
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": "5.36.0",
4
+ "version": "5.36.2",
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 = "5.36.0";
30
+ const ULTRA11Y_PLUGIN_VERSION = "5.36.2";
31
31
 
32
32
  const here = dirname(fileURLToPath(import.meta.url));
33
33
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ultra11y",
3
- "version": "5.36.0",
3
+ "version": "5.36.2",
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",
@@ -6,7 +6,7 @@ import { join as join53, relative as relative5, resolve as resolve16, sep as sep
6
6
  import { fileURLToPath as fileURLToPath5, pathToFileURL as pathToFileURL3 } from "url";
7
7
 
8
8
  // src/types.ts
9
- var VERSION = "5.36.0";
9
+ var VERSION = "5.36.2";
10
10
  var SCHEMA_VERSION = 2;
11
11
  var INAPPLICABLE_STATUS = "C";
12
12
 
@@ -58261,41 +58261,6 @@ function exhaustiveGrid(groups, standard, lang) {
58261
58261
  out2.push("");
58262
58262
  return out2;
58263
58263
  }
58264
- function splitReportSections(md) {
58265
- const lines = md.split("\n");
58266
- const preamble = [];
58267
- const sections = [];
58268
- let current = null;
58269
- let fence = null;
58270
- const push = (l) => {
58271
- if (current) current.push(l);
58272
- else preamble.push(l);
58273
- };
58274
- for (const line of lines) {
58275
- const f = /^\s*(```+|~~~+)/.exec(line);
58276
- if (f) {
58277
- const mark = f[1];
58278
- if (fence === null) fence = mark[0].repeat(mark.length);
58279
- else if (mark.startsWith(fence[0]) && mark.length >= fence.length) fence = null;
58280
- push(line);
58281
- continue;
58282
- }
58283
- if (fence === null && line.startsWith("## ")) {
58284
- current = [line];
58285
- const own = current;
58286
- sections.push({
58287
- heading: line,
58288
- lines: own,
58289
- get text() {
58290
- return own.join("\n");
58291
- }
58292
- });
58293
- continue;
58294
- }
58295
- push(line);
58296
- }
58297
- return { preamble, sections };
58298
- }
58299
58264
  function renderPageRates(r, pages, standard, lang) {
58300
58265
  if (!pages.length) return [];
58301
58266
  const s = L5[lang];
@@ -61363,13 +61328,18 @@ function subjectsForPackCriterion2(standard, id, scs) {
61363
61328
  for (const sc of scs) for (const subject of subjectsForSc2(sc)) if (!out2.includes(subject)) out2.push(subject);
61364
61329
  return out2;
61365
61330
  }
61331
+ function packResultNeedsAdjudication(pc, criterion) {
61332
+ if (pc.status === "manual") return true;
61333
+ const hasJudgmentTest = Object.values(criterion?.automation?.tests ?? {}).includes("judgment");
61334
+ return pc.status === INAPPLICABLE_STATUS && pc.inapplicable === true && pc.decidedBy !== "agent" && hasJudgmentTest;
61335
+ }
61366
61336
  function buildAdjudicationWorklist(audit2, opts = {}) {
61367
61337
  const docs = docsForAudit(audit2, opts.cwd);
61368
61338
  const standard = opts.standard;
61369
61339
  const limits = adjudicationLimits(opts.cwd);
61370
61340
  if (standard !== void 0 && !isCore(standard)) {
61371
61341
  const pack = loadPack(standard);
61372
- return derivePackResults(audit2, standard).filter((pc) => pc.status === "manual").map((pc) => {
61342
+ return derivePackResults(audit2, standard).filter((pc) => packResultNeedsAdjudication(pc, getCriterion(pack, pc.id))).map((pc) => {
61373
61343
  const crit = getCriterion(pack, pc.id);
61374
61344
  const scs = crit?.wcag ?? pc.scs;
61375
61345
  const item = blankItem(
@@ -61570,8 +61540,9 @@ function applyAdjudication(audit2, adj, opts = {}) {
61570
61540
  const packMode = !isCore(adj.standard);
61571
61541
  const open = /* @__PURE__ */ new Set();
61572
61542
  if (packMode) {
61543
+ const pack = loadPack(adj.standard);
61573
61544
  for (const pc of derivePackResults(audit2, adj.standard)) {
61574
- if (pc.status !== "manual") continue;
61545
+ if (!packResultNeedsAdjudication(pc, getCriterion(pack, pc.id))) continue;
61575
61546
  open.add(pc.id);
61576
61547
  if (byId2.has(pc.id)) continue;
61577
61548
  if (Object.hasOwn(expected, pc.id)) uncovered(pc.id);
@@ -66436,7 +66407,7 @@ var S = {
66436
66407
  noRenderedExecuted: "0 page rendue : aucun test rendered n'a \xE9t\xE9 ex\xE9cut\xE9",
66437
66408
  staticTested: (tests, ids) => `${tests} test(s) static \u2014 crit\xE8res : ${ids || "\u2014"}`,
66438
66409
  renderedContract: (tests, ids) => `${tests} test(s) rendered pr\xE9vus \u2014 crit\xE8res : ${ids || "\u2014"}`,
66439
- judgmentContract: (tests, criteria) => `${tests} test(s) judgment sur ${criteria} crit\xE8re(s), transmis \xE0 l'IA seulement quand ils restent applicables et non tranch\xE9s`
66410
+ judgmentContract: (tests, criteria) => `${tests} test(s) judgment sur ${criteria} crit\xE8re(s), tous transmis \xE0 l'IA tant qu'ils ne sont pas tranch\xE9s`
66440
66411
  },
66441
66412
  en: {
66442
66413
  title: "ultra11y accessibility audit",
@@ -66512,7 +66483,7 @@ var S = {
66512
66483
  noRenderedExecuted: "0 rendered pages: no rendered test was executed",
66513
66484
  staticTested: (tests, ids) => `${tests} static test(s) \u2014 criteria: ${ids || "\u2014"}`,
66514
66485
  renderedContract: (tests, ids) => `${tests} planned rendered test(s) \u2014 criteria: ${ids || "\u2014"}`,
66515
- judgmentContract: (tests, criteria) => `${tests} judgment test(s) across ${criteria} criterion(ia), sent to AI only while applicable and undecided`
66486
+ judgmentContract: (tests, criteria) => `${tests} judgment test(s) across ${criteria} criterion(ia), all sent to AI while undecided`
66516
66487
  }
66517
66488
  };
66518
66489
  var MAX_ROWS = 50;
@@ -66663,29 +66634,6 @@ function stepSummary(result, opts = {}) {
66663
66634
  if (unanchored) out2.push(`> ${s.unanchored(unanchored)}`, "");
66664
66635
  return out2.join("\n");
66665
66636
  }
66666
- function reportSectionsBody(result, standard, lang, budget, prefer) {
66667
- let md;
66668
- try {
66669
- md = isCore(standard) ? renderReport(result, lang) : renderPackReport(result, loadPack(standard), lang);
66670
- } catch {
66671
- return { body: [], dropped: [] };
66672
- }
66673
- const { sections } = splitReportSections(md);
66674
- const ordered = prefer ? [...sections.filter((x) => prefer.test(x.heading)), ...sections.filter((x) => !prefer.test(x.heading))] : sections;
66675
- const body3 = [];
66676
- const dropped = [];
66677
- let spent = 0;
66678
- for (const section of ordered) {
66679
- const text = section.text.trimEnd();
66680
- if (dropped.length || spent + sizeOf(text) + 2 > budget) {
66681
- dropped.push(section.heading.replace(/^##\s*/, ""));
66682
- continue;
66683
- }
66684
- body3.push(text, "");
66685
- spent += sizeOf(text) + 2;
66686
- }
66687
- return { body: body3, dropped };
66688
- }
66689
66637
  function prComment(result, opts = {}) {
66690
66638
  const standard = opts.standard ?? CORE2;
66691
66639
  const lang = opts.lang ?? "en";
@@ -66709,12 +66657,6 @@ function prComment(result, opts = {}) {
66709
66657
  const tail = [];
66710
66658
  if (opts.artifactName) tail.push(s.artifact(opts.artifactName), "");
66711
66659
  if (opts.runUrl) tail.push(s.runLink(opts.runUrl), "");
66712
- const fixed = sizeOf([...head, ...tail].join("\n"));
66713
- const { body: body3, dropped } = reportSectionsBody(result, standard, lang, Math.max(0, COMMENT_LIMIT - fixed - 512));
66714
- const notes = [];
66715
- if (dropped.length) notes.push(s.sectionsDropped(dropped), "");
66716
- const assembled = [...head, ...body3, ...notes, ...tail].join("\n").trimEnd();
66717
- if (sizeOf(assembled) <= COMMENT_LIMIT) return assembled;
66718
66660
  const assemble = (rows2) => {
66719
66661
  const digest = [];
66720
66662
  if (criteria.length) {
@@ -3,7 +3,7 @@ name: review-a11y
3
3
  description: "Use to REVIEW changed frontend code for accessibility: staged files, a working diff, branch, or PR; prompts such as 'review a11y', 'is this accessible?', or 'anything to fix before merge?'; and hook or CI findings on a change. Run the bundled install-free engine with cross-file JSX/TSX analysis, then adjudicate candidates and return a severity-ranked WCAG 2.2 AA review with file:line fixes, residual rendering risks, and a verdict. Use `ultra11y` instead for whole-repo audits, dated conformance reports, country standards, PRD backlogs, or authoring accessible markup."
4
4
  license: MIT
5
5
  metadata:
6
- version: 5.36.0
6
+ version: 5.36.2
7
7
  ---
8
8
 
9
9
  # review-a11y — accessibility review of changed code
@@ -6,7 +6,7 @@ import { join as join53, relative as relative5, resolve as resolve16, sep as sep
6
6
  import { fileURLToPath as fileURLToPath5, pathToFileURL as pathToFileURL3 } from "url";
7
7
 
8
8
  // src/types.ts
9
- var VERSION = "5.36.0";
9
+ var VERSION = "5.36.2";
10
10
  var SCHEMA_VERSION = 2;
11
11
  var INAPPLICABLE_STATUS = "C";
12
12
 
@@ -58261,41 +58261,6 @@ function exhaustiveGrid(groups, standard, lang) {
58261
58261
  out2.push("");
58262
58262
  return out2;
58263
58263
  }
58264
- function splitReportSections(md) {
58265
- const lines = md.split("\n");
58266
- const preamble = [];
58267
- const sections = [];
58268
- let current = null;
58269
- let fence = null;
58270
- const push = (l) => {
58271
- if (current) current.push(l);
58272
- else preamble.push(l);
58273
- };
58274
- for (const line of lines) {
58275
- const f = /^\s*(```+|~~~+)/.exec(line);
58276
- if (f) {
58277
- const mark = f[1];
58278
- if (fence === null) fence = mark[0].repeat(mark.length);
58279
- else if (mark.startsWith(fence[0]) && mark.length >= fence.length) fence = null;
58280
- push(line);
58281
- continue;
58282
- }
58283
- if (fence === null && line.startsWith("## ")) {
58284
- current = [line];
58285
- const own = current;
58286
- sections.push({
58287
- heading: line,
58288
- lines: own,
58289
- get text() {
58290
- return own.join("\n");
58291
- }
58292
- });
58293
- continue;
58294
- }
58295
- push(line);
58296
- }
58297
- return { preamble, sections };
58298
- }
58299
58264
  function renderPageRates(r, pages, standard, lang) {
58300
58265
  if (!pages.length) return [];
58301
58266
  const s = L5[lang];
@@ -61363,13 +61328,18 @@ function subjectsForPackCriterion2(standard, id, scs) {
61363
61328
  for (const sc of scs) for (const subject of subjectsForSc2(sc)) if (!out2.includes(subject)) out2.push(subject);
61364
61329
  return out2;
61365
61330
  }
61331
+ function packResultNeedsAdjudication(pc, criterion) {
61332
+ if (pc.status === "manual") return true;
61333
+ const hasJudgmentTest = Object.values(criterion?.automation?.tests ?? {}).includes("judgment");
61334
+ return pc.status === INAPPLICABLE_STATUS && pc.inapplicable === true && pc.decidedBy !== "agent" && hasJudgmentTest;
61335
+ }
61366
61336
  function buildAdjudicationWorklist(audit2, opts = {}) {
61367
61337
  const docs = docsForAudit(audit2, opts.cwd);
61368
61338
  const standard = opts.standard;
61369
61339
  const limits = adjudicationLimits(opts.cwd);
61370
61340
  if (standard !== void 0 && !isCore(standard)) {
61371
61341
  const pack = loadPack(standard);
61372
- return derivePackResults(audit2, standard).filter((pc) => pc.status === "manual").map((pc) => {
61342
+ return derivePackResults(audit2, standard).filter((pc) => packResultNeedsAdjudication(pc, getCriterion(pack, pc.id))).map((pc) => {
61373
61343
  const crit = getCriterion(pack, pc.id);
61374
61344
  const scs = crit?.wcag ?? pc.scs;
61375
61345
  const item = blankItem(
@@ -61570,8 +61540,9 @@ function applyAdjudication(audit2, adj, opts = {}) {
61570
61540
  const packMode = !isCore(adj.standard);
61571
61541
  const open = /* @__PURE__ */ new Set();
61572
61542
  if (packMode) {
61543
+ const pack = loadPack(adj.standard);
61573
61544
  for (const pc of derivePackResults(audit2, adj.standard)) {
61574
- if (pc.status !== "manual") continue;
61545
+ if (!packResultNeedsAdjudication(pc, getCriterion(pack, pc.id))) continue;
61575
61546
  open.add(pc.id);
61576
61547
  if (byId2.has(pc.id)) continue;
61577
61548
  if (Object.hasOwn(expected, pc.id)) uncovered(pc.id);
@@ -66436,7 +66407,7 @@ var S = {
66436
66407
  noRenderedExecuted: "0 page rendue : aucun test rendered n'a \xE9t\xE9 ex\xE9cut\xE9",
66437
66408
  staticTested: (tests, ids) => `${tests} test(s) static \u2014 crit\xE8res : ${ids || "\u2014"}`,
66438
66409
  renderedContract: (tests, ids) => `${tests} test(s) rendered pr\xE9vus \u2014 crit\xE8res : ${ids || "\u2014"}`,
66439
- judgmentContract: (tests, criteria) => `${tests} test(s) judgment sur ${criteria} crit\xE8re(s), transmis \xE0 l'IA seulement quand ils restent applicables et non tranch\xE9s`
66410
+ judgmentContract: (tests, criteria) => `${tests} test(s) judgment sur ${criteria} crit\xE8re(s), tous transmis \xE0 l'IA tant qu'ils ne sont pas tranch\xE9s`
66440
66411
  },
66441
66412
  en: {
66442
66413
  title: "ultra11y accessibility audit",
@@ -66512,7 +66483,7 @@ var S = {
66512
66483
  noRenderedExecuted: "0 rendered pages: no rendered test was executed",
66513
66484
  staticTested: (tests, ids) => `${tests} static test(s) \u2014 criteria: ${ids || "\u2014"}`,
66514
66485
  renderedContract: (tests, ids) => `${tests} planned rendered test(s) \u2014 criteria: ${ids || "\u2014"}`,
66515
- judgmentContract: (tests, criteria) => `${tests} judgment test(s) across ${criteria} criterion(ia), sent to AI only while applicable and undecided`
66486
+ judgmentContract: (tests, criteria) => `${tests} judgment test(s) across ${criteria} criterion(ia), all sent to AI while undecided`
66516
66487
  }
66517
66488
  };
66518
66489
  var MAX_ROWS = 50;
@@ -66663,29 +66634,6 @@ function stepSummary(result, opts = {}) {
66663
66634
  if (unanchored) out2.push(`> ${s.unanchored(unanchored)}`, "");
66664
66635
  return out2.join("\n");
66665
66636
  }
66666
- function reportSectionsBody(result, standard, lang, budget, prefer) {
66667
- let md;
66668
- try {
66669
- md = isCore(standard) ? renderReport(result, lang) : renderPackReport(result, loadPack(standard), lang);
66670
- } catch {
66671
- return { body: [], dropped: [] };
66672
- }
66673
- const { sections } = splitReportSections(md);
66674
- const ordered = prefer ? [...sections.filter((x) => prefer.test(x.heading)), ...sections.filter((x) => !prefer.test(x.heading))] : sections;
66675
- const body3 = [];
66676
- const dropped = [];
66677
- let spent = 0;
66678
- for (const section of ordered) {
66679
- const text = section.text.trimEnd();
66680
- if (dropped.length || spent + sizeOf(text) + 2 > budget) {
66681
- dropped.push(section.heading.replace(/^##\s*/, ""));
66682
- continue;
66683
- }
66684
- body3.push(text, "");
66685
- spent += sizeOf(text) + 2;
66686
- }
66687
- return { body: body3, dropped };
66688
- }
66689
66637
  function prComment(result, opts = {}) {
66690
66638
  const standard = opts.standard ?? CORE2;
66691
66639
  const lang = opts.lang ?? "en";
@@ -66709,12 +66657,6 @@ function prComment(result, opts = {}) {
66709
66657
  const tail = [];
66710
66658
  if (opts.artifactName) tail.push(s.artifact(opts.artifactName), "");
66711
66659
  if (opts.runUrl) tail.push(s.runLink(opts.runUrl), "");
66712
- const fixed = sizeOf([...head, ...tail].join("\n"));
66713
- const { body: body3, dropped } = reportSectionsBody(result, standard, lang, Math.max(0, COMMENT_LIMIT - fixed - 512));
66714
- const notes = [];
66715
- if (dropped.length) notes.push(s.sectionsDropped(dropped), "");
66716
- const assembled = [...head, ...body3, ...notes, ...tail].join("\n").trimEnd();
66717
- if (sizeOf(assembled) <= COMMENT_LIMIT) return assembled;
66718
66660
  const assemble = (rows2) => {
66719
66661
  const digest = [];
66720
66662
  if (criteria.length) {
@@ -3,7 +3,7 @@ name: ultra11y
3
3
  description: "Use to AUDIT a repository, site, or page against WCAG 2.2 AA or a country standard such as RGAA; produce a dated conformance report, criterion grid, PRD backlog, or tickets; AUTHOR accessible markup; fix accessibility; or wire a repo gate and rendered-DOM capture pipeline. The bundled install-free engine runs 81 static checks tied to success criteria, while the agent adjudicates judgment criteria and routes rendering criteria to browser scans; check/verify reject invented non-conformities. Use `review-a11y` instead for a review scoped only to a diff, branch, or PR."
4
4
  license: MIT
5
5
  metadata:
6
- version: 5.36.0
6
+ version: 5.36.2
7
7
  ---
8
8
 
9
9
  # ultra11y — audit WCAG 2.2 AA and write accessible markup
@@ -132,7 +132,7 @@ steps:
132
132
  | input | default | |
133
133
  |---|---|---|
134
134
  | `adjudicate` | `none` | `api` · `agent` · `none` |
135
- | `adjudicate-runner` | `action` | WHO drives the agent tier: `action` (claude-code-action) or `cli` (the engine spawns `claude -p`) |
135
+ | `adjudicate-runner` | `cli` | WHO drives the agent tier: `cli` (read-only `claude -p`, shared with GitLab) or the historical `action` transport (claude-code-action) |
136
136
  | `adjudicate-grain` | `worklist` | how much the model rules on at once: `worklist` (batches of 8) or `criterion` (one call each). Needs `adjudicate-runner: cli` |
137
137
  | `adjudicate-budget-usd` | *(empty)* | dollar ceiling per `cli` invocation |
138
138
  | `adjudicate-model` | *(empty)* | model id for `api`; else `$ULTRA11Y_LLM_MODEL`, else the engine default |
@@ -6,7 +6,7 @@ import { join as join53, relative as relative5, resolve as resolve16, sep as sep
6
6
  import { fileURLToPath as fileURLToPath5, pathToFileURL as pathToFileURL3 } from "url";
7
7
 
8
8
  // src/types.ts
9
- var VERSION = "5.36.0";
9
+ var VERSION = "5.36.2";
10
10
  var SCHEMA_VERSION = 2;
11
11
  var INAPPLICABLE_STATUS = "C";
12
12
 
@@ -58261,41 +58261,6 @@ function exhaustiveGrid(groups, standard, lang) {
58261
58261
  out2.push("");
58262
58262
  return out2;
58263
58263
  }
58264
- function splitReportSections(md) {
58265
- const lines = md.split("\n");
58266
- const preamble = [];
58267
- const sections = [];
58268
- let current = null;
58269
- let fence = null;
58270
- const push = (l) => {
58271
- if (current) current.push(l);
58272
- else preamble.push(l);
58273
- };
58274
- for (const line of lines) {
58275
- const f = /^\s*(```+|~~~+)/.exec(line);
58276
- if (f) {
58277
- const mark = f[1];
58278
- if (fence === null) fence = mark[0].repeat(mark.length);
58279
- else if (mark.startsWith(fence[0]) && mark.length >= fence.length) fence = null;
58280
- push(line);
58281
- continue;
58282
- }
58283
- if (fence === null && line.startsWith("## ")) {
58284
- current = [line];
58285
- const own = current;
58286
- sections.push({
58287
- heading: line,
58288
- lines: own,
58289
- get text() {
58290
- return own.join("\n");
58291
- }
58292
- });
58293
- continue;
58294
- }
58295
- push(line);
58296
- }
58297
- return { preamble, sections };
58298
- }
58299
58264
  function renderPageRates(r, pages, standard, lang) {
58300
58265
  if (!pages.length) return [];
58301
58266
  const s = L5[lang];
@@ -61363,13 +61328,18 @@ function subjectsForPackCriterion2(standard, id, scs) {
61363
61328
  for (const sc of scs) for (const subject of subjectsForSc2(sc)) if (!out2.includes(subject)) out2.push(subject);
61364
61329
  return out2;
61365
61330
  }
61331
+ function packResultNeedsAdjudication(pc, criterion) {
61332
+ if (pc.status === "manual") return true;
61333
+ const hasJudgmentTest = Object.values(criterion?.automation?.tests ?? {}).includes("judgment");
61334
+ return pc.status === INAPPLICABLE_STATUS && pc.inapplicable === true && pc.decidedBy !== "agent" && hasJudgmentTest;
61335
+ }
61366
61336
  function buildAdjudicationWorklist(audit2, opts = {}) {
61367
61337
  const docs = docsForAudit(audit2, opts.cwd);
61368
61338
  const standard = opts.standard;
61369
61339
  const limits = adjudicationLimits(opts.cwd);
61370
61340
  if (standard !== void 0 && !isCore(standard)) {
61371
61341
  const pack = loadPack(standard);
61372
- return derivePackResults(audit2, standard).filter((pc) => pc.status === "manual").map((pc) => {
61342
+ return derivePackResults(audit2, standard).filter((pc) => packResultNeedsAdjudication(pc, getCriterion(pack, pc.id))).map((pc) => {
61373
61343
  const crit = getCriterion(pack, pc.id);
61374
61344
  const scs = crit?.wcag ?? pc.scs;
61375
61345
  const item = blankItem(
@@ -61570,8 +61540,9 @@ function applyAdjudication(audit2, adj, opts = {}) {
61570
61540
  const packMode = !isCore(adj.standard);
61571
61541
  const open = /* @__PURE__ */ new Set();
61572
61542
  if (packMode) {
61543
+ const pack = loadPack(adj.standard);
61573
61544
  for (const pc of derivePackResults(audit2, adj.standard)) {
61574
- if (pc.status !== "manual") continue;
61545
+ if (!packResultNeedsAdjudication(pc, getCriterion(pack, pc.id))) continue;
61575
61546
  open.add(pc.id);
61576
61547
  if (byId2.has(pc.id)) continue;
61577
61548
  if (Object.hasOwn(expected, pc.id)) uncovered(pc.id);
@@ -66436,7 +66407,7 @@ var S = {
66436
66407
  noRenderedExecuted: "0 page rendue : aucun test rendered n'a \xE9t\xE9 ex\xE9cut\xE9",
66437
66408
  staticTested: (tests, ids) => `${tests} test(s) static \u2014 crit\xE8res : ${ids || "\u2014"}`,
66438
66409
  renderedContract: (tests, ids) => `${tests} test(s) rendered pr\xE9vus \u2014 crit\xE8res : ${ids || "\u2014"}`,
66439
- judgmentContract: (tests, criteria) => `${tests} test(s) judgment sur ${criteria} crit\xE8re(s), transmis \xE0 l'IA seulement quand ils restent applicables et non tranch\xE9s`
66410
+ judgmentContract: (tests, criteria) => `${tests} test(s) judgment sur ${criteria} crit\xE8re(s), tous transmis \xE0 l'IA tant qu'ils ne sont pas tranch\xE9s`
66440
66411
  },
66441
66412
  en: {
66442
66413
  title: "ultra11y accessibility audit",
@@ -66512,7 +66483,7 @@ var S = {
66512
66483
  noRenderedExecuted: "0 rendered pages: no rendered test was executed",
66513
66484
  staticTested: (tests, ids) => `${tests} static test(s) \u2014 criteria: ${ids || "\u2014"}`,
66514
66485
  renderedContract: (tests, ids) => `${tests} planned rendered test(s) \u2014 criteria: ${ids || "\u2014"}`,
66515
- judgmentContract: (tests, criteria) => `${tests} judgment test(s) across ${criteria} criterion(ia), sent to AI only while applicable and undecided`
66486
+ judgmentContract: (tests, criteria) => `${tests} judgment test(s) across ${criteria} criterion(ia), all sent to AI while undecided`
66516
66487
  }
66517
66488
  };
66518
66489
  var MAX_ROWS = 50;
@@ -66663,29 +66634,6 @@ function stepSummary(result, opts = {}) {
66663
66634
  if (unanchored) out2.push(`> ${s.unanchored(unanchored)}`, "");
66664
66635
  return out2.join("\n");
66665
66636
  }
66666
- function reportSectionsBody(result, standard, lang, budget, prefer) {
66667
- let md;
66668
- try {
66669
- md = isCore(standard) ? renderReport(result, lang) : renderPackReport(result, loadPack(standard), lang);
66670
- } catch {
66671
- return { body: [], dropped: [] };
66672
- }
66673
- const { sections } = splitReportSections(md);
66674
- const ordered = prefer ? [...sections.filter((x) => prefer.test(x.heading)), ...sections.filter((x) => !prefer.test(x.heading))] : sections;
66675
- const body3 = [];
66676
- const dropped = [];
66677
- let spent = 0;
66678
- for (const section of ordered) {
66679
- const text = section.text.trimEnd();
66680
- if (dropped.length || spent + sizeOf(text) + 2 > budget) {
66681
- dropped.push(section.heading.replace(/^##\s*/, ""));
66682
- continue;
66683
- }
66684
- body3.push(text, "");
66685
- spent += sizeOf(text) + 2;
66686
- }
66687
- return { body: body3, dropped };
66688
- }
66689
66637
  function prComment(result, opts = {}) {
66690
66638
  const standard = opts.standard ?? CORE2;
66691
66639
  const lang = opts.lang ?? "en";
@@ -66709,12 +66657,6 @@ function prComment(result, opts = {}) {
66709
66657
  const tail = [];
66710
66658
  if (opts.artifactName) tail.push(s.artifact(opts.artifactName), "");
66711
66659
  if (opts.runUrl) tail.push(s.runLink(opts.runUrl), "");
66712
- const fixed = sizeOf([...head, ...tail].join("\n"));
66713
- const { body: body3, dropped } = reportSectionsBody(result, standard, lang, Math.max(0, COMMENT_LIMIT - fixed - 512));
66714
- const notes = [];
66715
- if (dropped.length) notes.push(s.sectionsDropped(dropped), "");
66716
- const assembled = [...head, ...body3, ...notes, ...tail].join("\n").trimEnd();
66717
- if (sizeOf(assembled) <= COMMENT_LIMIT) return assembled;
66718
66660
  const assemble = (rows2) => {
66719
66661
  const digest = [];
66720
66662
  if (criteria.length) {
package/src/adjudicate.ts CHANGED
@@ -400,12 +400,25 @@ function subjectsForPackCriterion(standard: StandardId, id: string, scs: string[
400
400
  return out;
401
401
  }
402
402
 
403
+ /** Whether a pack result still needs an agent ruling.
404
+ *
405
+ * Manual rows are the ordinary residual set. A judgment criterion provisionally closed as
406
+ * inapplicable is included as well: absence of a harvested subject is useful deterministic
407
+ * evidence, but a FULL audit still asks the agent to confirm that absence and the criterion's
408
+ * particular cases. A prior agent decision is never reopened. */
409
+ function packResultNeedsAdjudication(pc: ReturnType<typeof derivePackResults>[number], criterion?: PackCriterion): boolean {
410
+ if (pc.status === "manual") return true;
411
+ const hasJudgmentTest = Object.values(criterion?.automation?.tests ?? {}).includes("judgment");
412
+ return pc.status === INAPPLICABLE_STATUS && pc.inapplicable === true && pc.decidedBy !== "agent" && hasJudgmentTest;
413
+ }
414
+
403
415
  /** Build the adjudication worklist.
404
416
  *
405
417
  * For the WCAG core: one item per residual-risk (manual) success criterion.
406
418
  *
407
- * For a COUNTRY STANDARD: one item per PACK criterion that derives `manual` — which is where
408
- * almost the whole standard lives (97 of RGAA's 106 criteria carry judgment tests).
419
+ * For a COUNTRY STANDARD: one item per PACK criterion that derives `manual`, plus every
420
+ * judgment criterion provisionally closed for absence — 97 of RGAA's 106 criteria carry
421
+ * judgment tests, and all 97 pass through the agent unless already decided or definitively NC.
409
422
  * Keying by the pack's own criteria is not cosmetic: it is what lets an item carry the
410
423
  * criterion's numbered tests, and therefore what lets `normativeRefResolves` check a citation
411
424
  * against THIS criterion's tests instead of accepting any id of the right shape. */
@@ -417,7 +430,7 @@ export function buildAdjudicationWorklist(audit: AuditResult, opts: { cwd?: stri
417
430
  if (standard !== undefined && !isCore(standard)) {
418
431
  const pack = loadPack(standard);
419
432
  return derivePackResults(audit, standard)
420
- .filter((pc) => pc.status === "manual")
433
+ .filter((pc) => packResultNeedsAdjudication(pc, getCriterion(pack, pc.id)))
421
434
  .map((pc) => {
422
435
  const crit = getCriterion(pack, pc.id);
423
436
  const scs = crit?.wcag ?? pc.scs;
@@ -816,13 +829,13 @@ export function applyAdjudication(
816
829
  const byId = new Map(adj.items.map((it) => [it.criteriaId, it]));
817
830
 
818
831
  // Coverage. Under the core that means every residual success criterion; under a pack it
819
- // means every pack criterion that derives `manual` — the pack's own granularity, which is
820
- // what the worklist was built at.
832
+ // mirrors `buildAdjudicationWorklist`, including provisionally-inapplicable judgment rows.
821
833
  const packMode = !isCore(adj.standard);
822
834
  const open = new Set<string>();
823
835
  if (packMode) {
836
+ const pack = loadPack(adj.standard);
824
837
  for (const pc of derivePackResults(audit, adj.standard)) {
825
- if (pc.status !== "manual") continue;
838
+ if (!packResultNeedsAdjudication(pc, getCriterion(pack, pc.id))) continue;
826
839
  open.add(pc.id);
827
840
  if (byId.has(pc.id)) continue;
828
841
  if (Object.hasOwn(expected, pc.id)) uncovered(pc.id);
package/src/annotate.ts CHANGED
@@ -22,7 +22,7 @@ import { resolveMessage, resolveRemediation } from "./messages.js";
22
22
  import { findingsForStandard } from "./standards/derive.js";
23
23
  import { packCriterionLabel } from "./standards/document.js";
24
24
  import { CORE, type StandardId, isCore, loadPack } from "./standards/index.js";
25
- import { automationOverview, packReportGroups, renderPackReport, renderReport, reportCoverage, reportGroups, splitReportSections } from "./report.js";
25
+ import { automationOverview, packReportGroups, reportCoverage, reportGroups } from "./report.js";
26
26
  import type { AuditResult, Finding, Lang, PageResult, Severity, Status } from "./types.js";
27
27
  import { isUrlPath, repoRelative } from "./util.js";
28
28
  import {
@@ -192,7 +192,7 @@ const S = {
192
192
  staticTested: (tests: number, ids: string) => `${tests} test(s) static — critères : ${ids || "—"}`,
193
193
  renderedContract: (tests: number, ids: string) => `${tests} test(s) rendered prévus — critères : ${ids || "—"}`,
194
194
  judgmentContract: (tests: number, criteria: number) =>
195
- `${tests} test(s) judgment sur ${criteria} critère(s), transmis à l'IA seulement quand ils restent applicables et non tranchés`,
195
+ `${tests} test(s) judgment sur ${criteria} critère(s), tous transmis à l'IA tant qu'ils ne sont pas tranchés`,
196
196
  },
197
197
  en: {
198
198
  title: "ultra11y accessibility audit",
@@ -281,8 +281,7 @@ const S = {
281
281
  noRenderedExecuted: "0 rendered pages: no rendered test was executed",
282
282
  staticTested: (tests: number, ids: string) => `${tests} static test(s) — criteria: ${ids || "—"}`,
283
283
  renderedContract: (tests: number, ids: string) => `${tests} planned rendered test(s) — criteria: ${ids || "—"}`,
284
- judgmentContract: (tests: number, criteria: number) =>
285
- `${tests} judgment test(s) across ${criteria} criterion(ia), sent to AI only while applicable and undecided`,
284
+ judgmentContract: (tests: number, criteria: number) => `${tests} judgment test(s) across ${criteria} criterion(ia), all sent to AI while undecided`,
286
285
  },
287
286
  } as const;
288
287
 
@@ -541,60 +540,6 @@ export function stepSummary(result: AuditResult, opts: AnnotateOptions = {}): st
541
540
  return out.join("\n");
542
541
  }
543
542
 
544
- /** THE COMMENT'S BODY IS THE REPORT'S OWN SECTIONS.
545
- *
546
- * A pull-request comment used to be a document of its own — a digest, written separately from
547
- * the audit it summarised. Two documents about one run drift, and a reader who opens the
548
- * artifact after reading the comment should recognise what they are looking at.
549
- *
550
- * So the body is the report, rendered once and split into its `##` sections, kept in report
551
- * order while the budget lasts. What does not fit is dropped WHOLE and NAMED — a heading a
552
- * reader can go and find in the artifact — never sliced at a byte offset, which lands
553
- * mid-table or inside a fence.
554
- *
555
- * The report's own preamble is skipped: the comment's head already carries the date, the
556
- * scope and the rate, and says them in the verdict's voice.
557
- *
558
- * `budget` is what is left after the head and the tail, both of which are never candidates
559
- * for dropping — a comment that fits but says nothing about where to look is worse than no
560
- * comment at all. */
561
- function reportSectionsBody(
562
- result: AuditResult,
563
- standard: StandardId,
564
- lang: Lang,
565
- budget: number,
566
- /** Headings to take FIRST, still in report order. Not a reordering of the audit: the two
567
- * comments answer different questions of the same document, and the one that answers
568
- * "which pages conform" must not lose the page sections to a budget spent on the defect
569
- * list. What is dropped is named either way. */
570
- prefer?: RegExp,
571
- ): { body: string[]; dropped: string[] } {
572
- let md: string;
573
- try {
574
- md = isCore(standard) ? renderReport(result, lang) : renderPackReport(result, loadPack(standard), lang);
575
- } catch {
576
- // A rendering failure must never cost the comment. The caller still has its head and tail,
577
- // which carry the verdict and the link — the two things a reviewer cannot do without.
578
- return { body: [], dropped: [] };
579
- }
580
- const { sections } = splitReportSections(md);
581
- const ordered = prefer ? [...sections.filter((x) => prefer.test(x.heading)), ...sections.filter((x) => !prefer.test(x.heading))] : sections;
582
- const body: string[] = [];
583
- const dropped: string[] = [];
584
- let spent = 0;
585
- for (const section of ordered) {
586
- const text = section.text.trimEnd();
587
- // +2 for the blank line that joins it to what precedes.
588
- if (dropped.length || spent + sizeOf(text) + 2 > budget) {
589
- dropped.push(section.heading.replace(/^##\s*/, ""));
590
- continue;
591
- }
592
- body.push(text, "");
593
- spent += sizeOf(text) + 2;
594
- }
595
- return { body, dropped };
596
- }
597
-
598
543
  /** The pull-request digest.
599
544
  *
600
545
  * Deliberately NOT the job summary. A reviewer wants the verdict, how much of the standard
@@ -636,19 +581,10 @@ export function prComment(result: AuditResult, opts: AnnotateOptions & { runUrl?
636
581
  // that comes down until the whole document fits, and the verdict, the rate and the link are
637
582
  // never candidates: a comment that fits but says nothing about where to look is worse than
638
583
  // no comment at all.
639
- // The body is the REPORT's own sections — same document, same words, same order — kept while
640
- // the budget lasts and dropped whole otherwise. The head and the tail are never candidates.
641
- const fixed = sizeOf([...head, ...tail].join("\n"));
642
- const { body, dropped } = reportSectionsBody(result, standard, lang, Math.max(0, COMMENT_LIMIT - fixed - 512));
643
- const notes: string[] = [];
644
- if (dropped.length) notes.push(s.sectionsDropped(dropped), "");
645
-
646
- const assembled = [...head, ...body, ...notes, ...tail].join("\n").trimEnd();
647
- if (sizeOf(assembled) <= COMMENT_LIMIT) return assembled;
648
-
649
- // The report could not be cut small enough — a single section larger than the whole budget.
650
- // Fall back to the digest this comment carried before: the distinct defects and a link, which
651
- // is the least a reviewer needs. Never a byte-slice of the report.
584
+ // Keep the comment deliberately compact: scope + tested criteria + actionable defects. The
585
+ // exhaustive 106-row grid belongs to the artifact, where it is searchable and does not bury
586
+ // the PR verdict. Whole criterion blocks are removed from the end if GitHub's limit requires
587
+ // it; the verdict, execution scope and artifact/run links always survive.
652
588
  const assemble = (rows: number): string => {
653
589
  const digest: string[] = [];
654
590
  if (criteria.length) {
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 = "5.36.0";
3
+ export const VERSION = "5.36.2";
4
4
  export const SCHEMA_VERSION = 2;
5
5
 
6
6
  // Lang = the UI FRAME's language — the `L` localization tables each module (report,