ultra11y 5.37.0 → 5.37.1

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.37.0",
13
+ "version": "5.37.1",
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.37.0",
4
+ "version": "5.37.1",
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.37.0",
4
+ "version": "5.37.1",
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.37.0";
30
+ const ULTRA11Y_PLUGIN_VERSION = "5.37.1";
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.37.0",
3
+ "version": "5.37.1",
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.37.0";
9
+ var VERSION = "5.37.1";
10
10
  var SCHEMA_VERSION = 2;
11
11
  var INAPPLICABLE_STATUS = "C";
12
12
 
@@ -56362,6 +56362,10 @@ import { mkdirSync as mkdirSync6, writeFileSync as writeFileSync7 } from "fs";
56362
56362
  import { join as join31 } from "path";
56363
56363
 
56364
56364
  // src/standards/derive.ts
56365
+ function isProvisionalJudgmentInapplicable(result, criterion) {
56366
+ if (result.status !== INAPPLICABLE_STATUS || result.inapplicable !== true || result.decidedBy === "agent") return false;
56367
+ return Object.values(criterion?.automation?.tests ?? {}).includes("judgment");
56368
+ }
56365
56369
  function aggregate2(results) {
56366
56370
  if (results.some((r) => r.status === "NC")) return "NC";
56367
56371
  if (results.some((r) => r.status === "C" && !r.inapplicable)) return "C";
@@ -57687,7 +57691,12 @@ function pageGridModel(result, derived, standard, lang) {
57687
57691
  }
57688
57692
  const pack = loadPack(standard);
57689
57693
  const rows = pack.criteria.map((pc) => ({ id: pc.id, label: pc.id, group: `${pc.theme}. ${themeName(pack, pc.theme, lang) ?? ""}`.trim() }));
57690
- for (const p of derived) for (const pc of derivePackResults(pageView(result, p), standard, p.id)) put(pc.id, p.id, pc.status);
57694
+ for (const p of derived) {
57695
+ for (const pc of derivePackResults(pageView(result, p), standard, p.id)) {
57696
+ const criterion = pack.criteria.find((row) => row.id === pc.id);
57697
+ put(pc.id, p.id, isProvisionalJudgmentInapplicable(pc, criterion) ? "manual" : pc.status);
57698
+ }
57699
+ }
57691
57700
  return { rows, status };
57692
57701
  }
57693
57702
  function renderRedirected(redirected, lang = "en") {
@@ -57857,15 +57866,19 @@ function pageCriterionRows(result, page, standard, lang) {
57857
57866
  }
57858
57867
  const pack = loadPack(standard);
57859
57868
  const byId2 = new Map(derivePackResults(pageView(result, page), standard, page.id).map((r) => [r.id, r]));
57860
- return pack.criteria.map((pc) => ({
57861
- id: pc.id,
57862
- label: `${pc.id} \u2014 ${titlePlain(pack, pc, lang)}`,
57863
- group: `${pc.theme}. ${themeName(pack, pc.theme, lang) ?? ""}`.trim(),
57864
- status: byId2.get(pc.id)?.status ?? "manual",
57865
- tests: packTestIds(pack, pc.id),
57866
- decidedBy: byId2.get(pc.id)?.decidedBy,
57867
- ...byId2.get(pc.id)?.inapplicable ? { inapplicable: true } : {}
57868
- }));
57869
+ return pack.criteria.map((pc) => {
57870
+ const result2 = byId2.get(pc.id);
57871
+ const provisionalNa = result2 ? isProvisionalJudgmentInapplicable(result2, pc) : false;
57872
+ return {
57873
+ id: pc.id,
57874
+ label: `${pc.id} \u2014 ${titlePlain(pack, pc, lang)}`,
57875
+ group: `${pc.theme}. ${themeName(pack, pc.theme, lang) ?? ""}`.trim(),
57876
+ status: provisionalNa ? "manual" : result2?.status ?? "manual",
57877
+ tests: packTestIds(pack, pc.id),
57878
+ decidedBy: result2?.decidedBy,
57879
+ ...result2?.inapplicable && !provisionalNa ? { inapplicable: true } : {}
57880
+ };
57881
+ });
57869
57882
  }
57870
57883
  function pagesForStandard(result, pages, standard, lang) {
57871
57884
  if (isCore(standard)) return pages;
@@ -58446,19 +58459,21 @@ function packReportGroups(r, pack, lang = "en") {
58446
58459
  const derived = derivePackResults(r, pack.key);
58447
58460
  const s = L5[lang];
58448
58461
  const naReason = lang === "fr" ? "Rien de la nature de ce crit\xE8re n'est pr\xE9sent dans le p\xE9rim\xE8tre." : "Nothing of this criterion's kind is present in scope.";
58462
+ const provisionalNaReason = lang === "fr" ? "Aucun sujet d\xE9tect\xE9 par le moteur ; l'IA doit confirmer la non-applicabilit\xE9 de ce crit\xE8re de jugement." : "The engine detected no subject; the AI must confirm that this judgment criterion is not applicable.";
58449
58463
  const byTheme = /* @__PURE__ */ new Map();
58450
58464
  for (const pr of derived) {
58451
58465
  const pc = pack.criteria.find((c2) => c2.id === pr.id);
58466
+ const provisionalNa = isProvisionalJudgmentInapplicable(pr, pc);
58452
58467
  const row = {
58453
58468
  id: pr.id,
58454
58469
  label: `${pack.name} ${pr.id} \u2014 ${title(pack, pc, lang)}`,
58455
- status: pr.status,
58470
+ status: provisionalNa ? "manual" : pr.status,
58456
58471
  findings: pr.findings,
58457
58472
  ...pr.decidedBy ? { decidedBy: pr.decidedBy } : {},
58458
- ...pr.inapplicable ? { inapplicable: true } : {},
58473
+ ...pr.inapplicable && !provisionalNa ? { inapplicable: true } : {},
58459
58474
  // outOfScope / scopedOut criteria are "manual" with their own dedicated justification —
58460
58475
  // never mixed with the "nothing of that kind here" reason (see the manual section above).
58461
- ...pr.outOfScope ? { justification: s.outOfScope } : pr.scopedOut ? { justification: s.scopedOut } : pr.judgment ? { justification: s.judgment } : pr.inapplicable ? { justification: naReason } : {}
58476
+ ...provisionalNa ? { justification: provisionalNaReason } : pr.outOfScope ? { justification: s.outOfScope } : pr.scopedOut ? { justification: s.scopedOut } : pr.judgment ? { justification: s.judgment } : pr.inapplicable ? { justification: naReason } : {}
58462
58477
  };
58463
58478
  (byTheme.get(pr.theme) ?? byTheme.set(pr.theme, []).get(pr.theme)).push(row);
58464
58479
  }
@@ -61335,8 +61350,7 @@ function subjectsForPackCriterion2(standard, id, scs) {
61335
61350
  }
61336
61351
  function packResultNeedsAdjudication(pc, criterion) {
61337
61352
  if (pc.status === "manual") return true;
61338
- const hasJudgmentTest = Object.values(criterion?.automation?.tests ?? {}).includes("judgment");
61339
- return pc.status === INAPPLICABLE_STATUS && pc.inapplicable === true && pc.decidedBy !== "agent" && hasJudgmentTest;
61353
+ return isProvisionalJudgmentInapplicable(pc, criterion);
61340
61354
  }
61341
61355
  function buildAdjudicationWorklist(audit2, opts = {}) {
61342
61356
  const docs = docsForAudit(audit2, opts.cwd);
@@ -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.37.0
6
+ version: 5.37.1
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.37.0";
9
+ var VERSION = "5.37.1";
10
10
  var SCHEMA_VERSION = 2;
11
11
  var INAPPLICABLE_STATUS = "C";
12
12
 
@@ -56362,6 +56362,10 @@ import { mkdirSync as mkdirSync6, writeFileSync as writeFileSync7 } from "fs";
56362
56362
  import { join as join31 } from "path";
56363
56363
 
56364
56364
  // src/standards/derive.ts
56365
+ function isProvisionalJudgmentInapplicable(result, criterion) {
56366
+ if (result.status !== INAPPLICABLE_STATUS || result.inapplicable !== true || result.decidedBy === "agent") return false;
56367
+ return Object.values(criterion?.automation?.tests ?? {}).includes("judgment");
56368
+ }
56365
56369
  function aggregate2(results) {
56366
56370
  if (results.some((r) => r.status === "NC")) return "NC";
56367
56371
  if (results.some((r) => r.status === "C" && !r.inapplicable)) return "C";
@@ -57687,7 +57691,12 @@ function pageGridModel(result, derived, standard, lang) {
57687
57691
  }
57688
57692
  const pack = loadPack(standard);
57689
57693
  const rows = pack.criteria.map((pc) => ({ id: pc.id, label: pc.id, group: `${pc.theme}. ${themeName(pack, pc.theme, lang) ?? ""}`.trim() }));
57690
- for (const p of derived) for (const pc of derivePackResults(pageView(result, p), standard, p.id)) put(pc.id, p.id, pc.status);
57694
+ for (const p of derived) {
57695
+ for (const pc of derivePackResults(pageView(result, p), standard, p.id)) {
57696
+ const criterion = pack.criteria.find((row) => row.id === pc.id);
57697
+ put(pc.id, p.id, isProvisionalJudgmentInapplicable(pc, criterion) ? "manual" : pc.status);
57698
+ }
57699
+ }
57691
57700
  return { rows, status };
57692
57701
  }
57693
57702
  function renderRedirected(redirected, lang = "en") {
@@ -57857,15 +57866,19 @@ function pageCriterionRows(result, page, standard, lang) {
57857
57866
  }
57858
57867
  const pack = loadPack(standard);
57859
57868
  const byId2 = new Map(derivePackResults(pageView(result, page), standard, page.id).map((r) => [r.id, r]));
57860
- return pack.criteria.map((pc) => ({
57861
- id: pc.id,
57862
- label: `${pc.id} \u2014 ${titlePlain(pack, pc, lang)}`,
57863
- group: `${pc.theme}. ${themeName(pack, pc.theme, lang) ?? ""}`.trim(),
57864
- status: byId2.get(pc.id)?.status ?? "manual",
57865
- tests: packTestIds(pack, pc.id),
57866
- decidedBy: byId2.get(pc.id)?.decidedBy,
57867
- ...byId2.get(pc.id)?.inapplicable ? { inapplicable: true } : {}
57868
- }));
57869
+ return pack.criteria.map((pc) => {
57870
+ const result2 = byId2.get(pc.id);
57871
+ const provisionalNa = result2 ? isProvisionalJudgmentInapplicable(result2, pc) : false;
57872
+ return {
57873
+ id: pc.id,
57874
+ label: `${pc.id} \u2014 ${titlePlain(pack, pc, lang)}`,
57875
+ group: `${pc.theme}. ${themeName(pack, pc.theme, lang) ?? ""}`.trim(),
57876
+ status: provisionalNa ? "manual" : result2?.status ?? "manual",
57877
+ tests: packTestIds(pack, pc.id),
57878
+ decidedBy: result2?.decidedBy,
57879
+ ...result2?.inapplicable && !provisionalNa ? { inapplicable: true } : {}
57880
+ };
57881
+ });
57869
57882
  }
57870
57883
  function pagesForStandard(result, pages, standard, lang) {
57871
57884
  if (isCore(standard)) return pages;
@@ -58446,19 +58459,21 @@ function packReportGroups(r, pack, lang = "en") {
58446
58459
  const derived = derivePackResults(r, pack.key);
58447
58460
  const s = L5[lang];
58448
58461
  const naReason = lang === "fr" ? "Rien de la nature de ce crit\xE8re n'est pr\xE9sent dans le p\xE9rim\xE8tre." : "Nothing of this criterion's kind is present in scope.";
58462
+ const provisionalNaReason = lang === "fr" ? "Aucun sujet d\xE9tect\xE9 par le moteur ; l'IA doit confirmer la non-applicabilit\xE9 de ce crit\xE8re de jugement." : "The engine detected no subject; the AI must confirm that this judgment criterion is not applicable.";
58449
58463
  const byTheme = /* @__PURE__ */ new Map();
58450
58464
  for (const pr of derived) {
58451
58465
  const pc = pack.criteria.find((c2) => c2.id === pr.id);
58466
+ const provisionalNa = isProvisionalJudgmentInapplicable(pr, pc);
58452
58467
  const row = {
58453
58468
  id: pr.id,
58454
58469
  label: `${pack.name} ${pr.id} \u2014 ${title(pack, pc, lang)}`,
58455
- status: pr.status,
58470
+ status: provisionalNa ? "manual" : pr.status,
58456
58471
  findings: pr.findings,
58457
58472
  ...pr.decidedBy ? { decidedBy: pr.decidedBy } : {},
58458
- ...pr.inapplicable ? { inapplicable: true } : {},
58473
+ ...pr.inapplicable && !provisionalNa ? { inapplicable: true } : {},
58459
58474
  // outOfScope / scopedOut criteria are "manual" with their own dedicated justification —
58460
58475
  // never mixed with the "nothing of that kind here" reason (see the manual section above).
58461
- ...pr.outOfScope ? { justification: s.outOfScope } : pr.scopedOut ? { justification: s.scopedOut } : pr.judgment ? { justification: s.judgment } : pr.inapplicable ? { justification: naReason } : {}
58476
+ ...provisionalNa ? { justification: provisionalNaReason } : pr.outOfScope ? { justification: s.outOfScope } : pr.scopedOut ? { justification: s.scopedOut } : pr.judgment ? { justification: s.judgment } : pr.inapplicable ? { justification: naReason } : {}
58462
58477
  };
58463
58478
  (byTheme.get(pr.theme) ?? byTheme.set(pr.theme, []).get(pr.theme)).push(row);
58464
58479
  }
@@ -61335,8 +61350,7 @@ function subjectsForPackCriterion2(standard, id, scs) {
61335
61350
  }
61336
61351
  function packResultNeedsAdjudication(pc, criterion) {
61337
61352
  if (pc.status === "manual") return true;
61338
- const hasJudgmentTest = Object.values(criterion?.automation?.tests ?? {}).includes("judgment");
61339
- return pc.status === INAPPLICABLE_STATUS && pc.inapplicable === true && pc.decidedBy !== "agent" && hasJudgmentTest;
61353
+ return isProvisionalJudgmentInapplicable(pc, criterion);
61340
61354
  }
61341
61355
  function buildAdjudicationWorklist(audit2, opts = {}) {
61342
61356
  const docs = docsForAudit(audit2, opts.cwd);
@@ -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.37.0
6
+ version: 5.37.1
7
7
  ---
8
8
 
9
9
  # ultra11y — audit WCAG 2.2 AA and write accessible markup
@@ -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.37.0";
9
+ var VERSION = "5.37.1";
10
10
  var SCHEMA_VERSION = 2;
11
11
  var INAPPLICABLE_STATUS = "C";
12
12
 
@@ -56362,6 +56362,10 @@ import { mkdirSync as mkdirSync6, writeFileSync as writeFileSync7 } from "fs";
56362
56362
  import { join as join31 } from "path";
56363
56363
 
56364
56364
  // src/standards/derive.ts
56365
+ function isProvisionalJudgmentInapplicable(result, criterion) {
56366
+ if (result.status !== INAPPLICABLE_STATUS || result.inapplicable !== true || result.decidedBy === "agent") return false;
56367
+ return Object.values(criterion?.automation?.tests ?? {}).includes("judgment");
56368
+ }
56365
56369
  function aggregate2(results) {
56366
56370
  if (results.some((r) => r.status === "NC")) return "NC";
56367
56371
  if (results.some((r) => r.status === "C" && !r.inapplicable)) return "C";
@@ -57687,7 +57691,12 @@ function pageGridModel(result, derived, standard, lang) {
57687
57691
  }
57688
57692
  const pack = loadPack(standard);
57689
57693
  const rows = pack.criteria.map((pc) => ({ id: pc.id, label: pc.id, group: `${pc.theme}. ${themeName(pack, pc.theme, lang) ?? ""}`.trim() }));
57690
- for (const p of derived) for (const pc of derivePackResults(pageView(result, p), standard, p.id)) put(pc.id, p.id, pc.status);
57694
+ for (const p of derived) {
57695
+ for (const pc of derivePackResults(pageView(result, p), standard, p.id)) {
57696
+ const criterion = pack.criteria.find((row) => row.id === pc.id);
57697
+ put(pc.id, p.id, isProvisionalJudgmentInapplicable(pc, criterion) ? "manual" : pc.status);
57698
+ }
57699
+ }
57691
57700
  return { rows, status };
57692
57701
  }
57693
57702
  function renderRedirected(redirected, lang = "en") {
@@ -57857,15 +57866,19 @@ function pageCriterionRows(result, page, standard, lang) {
57857
57866
  }
57858
57867
  const pack = loadPack(standard);
57859
57868
  const byId2 = new Map(derivePackResults(pageView(result, page), standard, page.id).map((r) => [r.id, r]));
57860
- return pack.criteria.map((pc) => ({
57861
- id: pc.id,
57862
- label: `${pc.id} \u2014 ${titlePlain(pack, pc, lang)}`,
57863
- group: `${pc.theme}. ${themeName(pack, pc.theme, lang) ?? ""}`.trim(),
57864
- status: byId2.get(pc.id)?.status ?? "manual",
57865
- tests: packTestIds(pack, pc.id),
57866
- decidedBy: byId2.get(pc.id)?.decidedBy,
57867
- ...byId2.get(pc.id)?.inapplicable ? { inapplicable: true } : {}
57868
- }));
57869
+ return pack.criteria.map((pc) => {
57870
+ const result2 = byId2.get(pc.id);
57871
+ const provisionalNa = result2 ? isProvisionalJudgmentInapplicable(result2, pc) : false;
57872
+ return {
57873
+ id: pc.id,
57874
+ label: `${pc.id} \u2014 ${titlePlain(pack, pc, lang)}`,
57875
+ group: `${pc.theme}. ${themeName(pack, pc.theme, lang) ?? ""}`.trim(),
57876
+ status: provisionalNa ? "manual" : result2?.status ?? "manual",
57877
+ tests: packTestIds(pack, pc.id),
57878
+ decidedBy: result2?.decidedBy,
57879
+ ...result2?.inapplicable && !provisionalNa ? { inapplicable: true } : {}
57880
+ };
57881
+ });
57869
57882
  }
57870
57883
  function pagesForStandard(result, pages, standard, lang) {
57871
57884
  if (isCore(standard)) return pages;
@@ -58446,19 +58459,21 @@ function packReportGroups(r, pack, lang = "en") {
58446
58459
  const derived = derivePackResults(r, pack.key);
58447
58460
  const s = L5[lang];
58448
58461
  const naReason = lang === "fr" ? "Rien de la nature de ce crit\xE8re n'est pr\xE9sent dans le p\xE9rim\xE8tre." : "Nothing of this criterion's kind is present in scope.";
58462
+ const provisionalNaReason = lang === "fr" ? "Aucun sujet d\xE9tect\xE9 par le moteur ; l'IA doit confirmer la non-applicabilit\xE9 de ce crit\xE8re de jugement." : "The engine detected no subject; the AI must confirm that this judgment criterion is not applicable.";
58449
58463
  const byTheme = /* @__PURE__ */ new Map();
58450
58464
  for (const pr of derived) {
58451
58465
  const pc = pack.criteria.find((c2) => c2.id === pr.id);
58466
+ const provisionalNa = isProvisionalJudgmentInapplicable(pr, pc);
58452
58467
  const row = {
58453
58468
  id: pr.id,
58454
58469
  label: `${pack.name} ${pr.id} \u2014 ${title(pack, pc, lang)}`,
58455
- status: pr.status,
58470
+ status: provisionalNa ? "manual" : pr.status,
58456
58471
  findings: pr.findings,
58457
58472
  ...pr.decidedBy ? { decidedBy: pr.decidedBy } : {},
58458
- ...pr.inapplicable ? { inapplicable: true } : {},
58473
+ ...pr.inapplicable && !provisionalNa ? { inapplicable: true } : {},
58459
58474
  // outOfScope / scopedOut criteria are "manual" with their own dedicated justification —
58460
58475
  // never mixed with the "nothing of that kind here" reason (see the manual section above).
58461
- ...pr.outOfScope ? { justification: s.outOfScope } : pr.scopedOut ? { justification: s.scopedOut } : pr.judgment ? { justification: s.judgment } : pr.inapplicable ? { justification: naReason } : {}
58476
+ ...provisionalNa ? { justification: provisionalNaReason } : pr.outOfScope ? { justification: s.outOfScope } : pr.scopedOut ? { justification: s.scopedOut } : pr.judgment ? { justification: s.judgment } : pr.inapplicable ? { justification: naReason } : {}
58462
58477
  };
58463
58478
  (byTheme.get(pr.theme) ?? byTheme.set(pr.theme, []).get(pr.theme)).push(row);
58464
58479
  }
@@ -61335,8 +61350,7 @@ function subjectsForPackCriterion2(standard, id, scs) {
61335
61350
  }
61336
61351
  function packResultNeedsAdjudication(pc, criterion) {
61337
61352
  if (pc.status === "manual") return true;
61338
- const hasJudgmentTest = Object.values(criterion?.automation?.tests ?? {}).includes("judgment");
61339
- return pc.status === INAPPLICABLE_STATUS && pc.inapplicable === true && pc.decidedBy !== "agent" && hasJudgmentTest;
61353
+ return isProvisionalJudgmentInapplicable(pc, criterion);
61340
61354
  }
61341
61355
  function buildAdjudicationWorklist(audit2, opts = {}) {
61342
61356
  const docs = docsForAudit(audit2, opts.cwd);
package/src/adjudicate.ts CHANGED
@@ -31,6 +31,7 @@ import {
31
31
  hasId,
32
32
  getCriterion,
33
33
  derivePackResults,
34
+ isProvisionalJudgmentInapplicable,
34
35
  criterionUrl,
35
36
  glossaryAnchorsOf,
36
37
  localize,
@@ -408,8 +409,7 @@ function subjectsForPackCriterion(standard: StandardId, id: string, scs: string[
408
409
  * particular cases. A prior agent decision is never reopened. */
409
410
  function packResultNeedsAdjudication(pc: ReturnType<typeof derivePackResults>[number], criterion?: PackCriterion): boolean {
410
411
  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;
412
+ return isProvisionalJudgmentInapplicable(pc, criterion);
413
413
  }
414
414
 
415
415
  /** Build the adjudication worklist.
@@ -26,7 +26,17 @@
26
26
  import { prdUnits } from "./prd.js";
27
27
  import { renderAuditorUnit, type AuditorCropLookup } from "./auditor.js";
28
28
  import { agentMarkNote, basisLabel, formatRate, pageView, unattributedFindings } from "./pages.js";
29
- import { CORE, type StandardId, derivePackResults, isCore, loadPack, packTestIds, themeName, titlePlain } from "./standards/index.js";
29
+ import {
30
+ CORE,
31
+ type StandardId,
32
+ derivePackResults,
33
+ isCore,
34
+ isProvisionalJudgmentInapplicable,
35
+ loadPack,
36
+ packTestIds,
37
+ themeName,
38
+ titlePlain,
39
+ } from "./standards/index.js";
30
40
  import type { AuditResult, Lang, PageResult, Status } from "./types.js";
31
41
  import { compareSC, scTitle } from "./wcag.js";
32
42
 
@@ -202,15 +212,19 @@ export function pageCriterionRows(result: AuditResult, page: PageResult, standar
202
212
  }
203
213
  const pack = loadPack(standard);
204
214
  const byId = new Map(derivePackResults(pageView(result, page), standard, page.id).map((r) => [r.id, r]));
205
- return pack.criteria.map((pc) => ({
206
- id: pc.id,
207
- label: `${pc.id} — ${titlePlain(pack, pc, lang)}`,
208
- group: `${pc.theme}. ${themeName(pack, pc.theme, lang) ?? ""}`.trim(),
209
- status: byId.get(pc.id)?.status ?? "manual",
210
- tests: packTestIds(pack, pc.id),
211
- decidedBy: byId.get(pc.id)?.decidedBy,
212
- ...(byId.get(pc.id)?.inapplicable ? { inapplicable: true } : {}),
213
- }));
215
+ return pack.criteria.map((pc) => {
216
+ const result = byId.get(pc.id);
217
+ const provisionalNa = result ? isProvisionalJudgmentInapplicable(result, pc) : false;
218
+ return {
219
+ id: pc.id,
220
+ label: `${pc.id} — ${titlePlain(pack, pc, lang)}`,
221
+ group: `${pc.theme}. ${themeName(pack, pc.theme, lang) ?? ""}`.trim(),
222
+ status: provisionalNa ? ("manual" as const) : (result?.status ?? "manual"),
223
+ tests: packTestIds(pack, pc.id),
224
+ decidedBy: result?.decidedBy,
225
+ ...(result?.inapplicable && !provisionalNa ? { inapplicable: true } : {}),
226
+ };
227
+ });
214
228
  }
215
229
 
216
230
  /** The pages, PROJECTED ONTO THE ACTIVE STANDARD — criteria, coverage and rate.
package/src/pages.ts CHANGED
@@ -22,7 +22,7 @@
22
22
  // on that criterion. Reading it as `C` on every other page is how a page with no images
23
23
  // scored 100% on "does each image have a relevant alternative?". See `pageStatus`.
24
24
  import { snapshotPageId } from "./snapshot.js";
25
- import { CORE, type StandardId, derivePackResults, isCore, loadPack, themeName } from "./standards/index.js";
25
+ import { CORE, type StandardId, derivePackResults, isCore, isProvisionalJudgmentInapplicable, loadPack, themeName } from "./standards/index.js";
26
26
  import type { AuditResult, CriterionResult, Finding, Lang, PageCoverage, PageResult, PageScope, Status, ScanRedirect } from "./types.js";
27
27
  import { renderedProvesOn } from "./coverage.js";
28
28
  import { renderedRulesFor } from "./rules/rendered.js";
@@ -526,7 +526,12 @@ export function pageGridModel(result: AuditResult, derived: PageResult[], standa
526
526
 
527
527
  const pack = loadPack(standard);
528
528
  const rows = pack.criteria.map((pc) => ({ id: pc.id, label: pc.id, group: `${pc.theme}. ${themeName(pack, pc.theme, lang) ?? ""}`.trim() }));
529
- for (const p of derived) for (const pc of derivePackResults(pageView(result, p), standard, p.id)) put(pc.id, p.id, pc.status);
529
+ for (const p of derived) {
530
+ for (const pc of derivePackResults(pageView(result, p), standard, p.id)) {
531
+ const criterion = pack.criteria.find((row) => row.id === pc.id);
532
+ put(pc.id, p.id, isProvisionalJudgmentInapplicable(pc, criterion) ? "manual" : pc.status);
533
+ }
534
+ }
530
535
  return { rows, status };
531
536
  }
532
537
 
package/src/report.ts CHANGED
@@ -21,6 +21,7 @@ import {
21
21
  isCore,
22
22
  loadPack,
23
23
  derivePackResults,
24
+ isProvisionalJudgmentInapplicable,
24
25
  packCriteriaForFinding,
25
26
  packConformancePct,
26
27
  packTestIds,
@@ -788,27 +789,34 @@ export function packReportGroups(r: AuditResult, pack: StandardPack, lang: Lang
788
789
  const derived = derivePackResults(r, pack.key);
789
790
  const s = L[lang];
790
791
  const naReason = lang === "fr" ? "Rien de la nature de ce critère n'est présent dans le périmètre." : "Nothing of this criterion's kind is present in scope.";
792
+ const provisionalNaReason =
793
+ lang === "fr"
794
+ ? "Aucun sujet détecté par le moteur ; l'IA doit confirmer la non-applicabilité de ce critère de jugement."
795
+ : "The engine detected no subject; the AI must confirm that this judgment criterion is not applicable.";
791
796
  const byTheme = new Map<number, ReportRow[]>();
792
797
  for (const pr of derived) {
793
798
  const pc = pack.criteria.find((c) => c.id === pr.id)!;
799
+ const provisionalNa = isProvisionalJudgmentInapplicable(pr, pc);
794
800
  const row: ReportRow = {
795
801
  id: pr.id,
796
802
  label: `${pack.name} ${pr.id} — ${packTitle(pack, pc, lang)}`,
797
- status: pr.status,
803
+ status: provisionalNa ? "manual" : pr.status,
798
804
  findings: pr.findings,
799
805
  ...(pr.decidedBy ? { decidedBy: pr.decidedBy } : {}),
800
- ...(pr.inapplicable ? { inapplicable: true } : {}),
806
+ ...(pr.inapplicable && !provisionalNa ? { inapplicable: true } : {}),
801
807
  // outOfScope / scopedOut criteria are "manual" with their own dedicated justification —
802
808
  // never mixed with the "nothing of that kind here" reason (see the manual section above).
803
- ...(pr.outOfScope
804
- ? { justification: s.outOfScope }
805
- : pr.scopedOut
806
- ? { justification: s.scopedOut }
807
- : pr.judgment
808
- ? { justification: s.judgment }
809
- : pr.inapplicable
810
- ? { justification: naReason }
811
- : {}),
809
+ ...(provisionalNa
810
+ ? { justification: provisionalNaReason }
811
+ : pr.outOfScope
812
+ ? { justification: s.outOfScope }
813
+ : pr.scopedOut
814
+ ? { justification: s.scopedOut }
815
+ : pr.judgment
816
+ ? { justification: s.judgment }
817
+ : pr.inapplicable
818
+ ? { justification: naReason }
819
+ : {}),
812
820
  };
813
821
  (byTheme.get(pr.theme) ?? byTheme.set(pr.theme, []).get(pr.theme)!).push(row);
814
822
  }
@@ -45,6 +45,17 @@ export interface PackCriterionResult {
45
45
  inapplicable?: boolean;
46
46
  }
47
47
 
48
+ /** Whether subject harvesting only PROVISIONALLY closed a judgment criterion.
49
+ *
50
+ * The absence is useful evidence for the adjudicator, but it is not a published verdict:
51
+ * every official `judgment` test still has to pass through the agent. Keep the raw derived
52
+ * result intact for that worklist, while report/page projections use this predicate to show
53
+ * the row as open until an agent has confirmed `NA`. */
54
+ export function isProvisionalJudgmentInapplicable(result: PackCriterionResult, criterion?: PackCriterion): boolean {
55
+ if (result.status !== INAPPLICABLE_STATUS || result.inapplicable !== true || result.decidedBy === "agent") return false;
56
+ return Object.values(criterion?.automation?.tests ?? {}).includes("judgment");
57
+ }
58
+
48
59
  // NC dominates (a real failure anywhere fails the criterion); then a decided C; then
49
60
  // manual (residual); else nothing of that kind exists here. Mirrors the core's aggregation.
50
61
  //
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.37.0";
3
+ export const VERSION = "5.37.1";
4
4
  export const SCHEMA_VERSION = 2;
5
5
 
6
6
  // Lang = the UI FRAME's language — the `L` localization tables each module (report,