Fix selector waivers across live and visual scans

AI-assisted implementation by Codex at maintainer pbakaus request. Merge current main without dropping upstream oracle updates. Resolve component waivers while the actual visual candidate is in hand; carry stamps through sampling and screenshot fallback. Forward project selectors through the live prelude and per-page scan config, and keep waived findings out of direct/snapshot/offscreen UI groups. Add Rust and Node regressions. Rust workspace, rebuilt bundle/engine, full Bun/Node/oracle/plugin suite and real Chrome direct/snapshot checks passed. Deterministic full live E2E sweep is still running. Generated provider harness output intentionally omitted.
This commit is contained in:
Paul Bakaus
2026-09-14 18:32:00 -07:00
188 changed files with 20232 additions and 345 deletions
+10
View File
@@ -36,6 +36,7 @@ function __visualContrastOptions(options = {}, config = {}) {
: false;
return {
...options,
...(Array.isArray(config.ignoreSelectors) ? { ignoreSelectors: config.ignoreSelectors } : {}),
maxCandidates: Number.isFinite(options.visualContrastMaxCandidates)
? options.visualContrastMaxCandidates
: Number.isFinite(options.maxCandidates)
@@ -47,6 +48,15 @@ function __visualContrastOptions(options = {}, config = {}) {
};
}
// Engines keep waiver stamps for callers that report suppression counts.
// UI consumers render only reportable findings, including after visual passes.
function __reportableGroups(groups) {
return groups.map(group => ({
...group,
findings: group.findings.filter(finding => !finding.ignoredBy),
})).filter(group => group.findings.length > 0);
}
// The analyses the lazy pass watches: unresolved only because the text was
// outside the viewport, and addressable.
function __lazyVisualContrastCandidates(analyses) {
+2
View File
@@ -42,6 +42,7 @@ function createVisualContrast(IO) {
function collectVisualContrastCandidates(options = {}) {
return __p(IO.coreSync('collect_visual_contrast_candidates', __j({
ignoreSelectors: options.ignoreSelectors,
maxCandidates: options.maxCandidates,
imageOnly: options.imageOnly,
})));
@@ -49,6 +50,7 @@ function createVisualContrast(IO) {
async function collectVisualContrastCandidatesAsync(options = {}) {
return core('collect_visual_contrast_candidates', __j({
ignoreSelectors: options.ignoreSelectors,
maxCandidates: options.maxCandidates,
imageOnly: options.imageOnly,
}));
+3 -2
View File
@@ -118,7 +118,7 @@ if (IS_BROWSER && !__impeccable) {
};
function browserFindingsFromMap(groupMap) {
return [...groupMap.entries()].map(([el, findings]) => ({ el, findings }));
return __reportableGroups([...groupMap.entries()].map(([el, findings]) => ({ el, findings })));
}
function collectBrowserFindings() {
@@ -172,6 +172,7 @@ if (IS_BROWSER && !__impeccable) {
}
function addVisualContrastResult(groupMap, result, options = {}) {
if (result?.ignoredBy) return false;
const elId = __impeccable.visual_contrast_result_el(JSON.stringify(result));
const el = __el(elId);
if (!el) return false;
@@ -467,7 +468,7 @@ if (IS_BROWSER && !__impeccable) {
if (__impeccable.snapshot_has_needs()) out = { needs: JSON.parse(__impeccable.snapshot_take_needs()) };
rounds++;
}
const serialized = JSON.parse(__impeccable.serialize_findings(JSON.stringify(out.groups)));
const serialized = JSON.parse(__impeccable.serialize_findings(JSON.stringify(__reportableGroups(out.groups))));
const unknownStyleProps = JSON.parse(__impeccable.snapshot_unknown_style_props());
__impeccable.snapshot_clear();
return {
+3 -2
View File
@@ -123,12 +123,13 @@
}
function serialize(wasm, groups) {
return JSON.parse(wasm.serialize_findings(JSON.stringify(groups)));
return JSON.parse(wasm.serialize_findings(JSON.stringify(__reportableGroups(groups))));
}
// addVisualContrastResult over id-keyed groups: the two decisions are the
// core's; this only keeps the map.
function addVisualContrastResult(wasm, groups, result) {
if (result?.ignoredBy) return 0;
const elId = wasm.visual_contrast_result_el(JSON.stringify(result));
if (!elId) return 0;
let group = groups.find(g => g.el === elId);
@@ -149,7 +150,7 @@
const vc = createVisualContrast(IO);
const t0 = performance.now();
const collected = JSON.parse(await IO.core('collect_browser_findings', configJson(config)));
const groups = collected.groups;
const groups = __reportableGroups(collected.groups);
const stats = { elements: n, coreMs: performance.now() - t0, unknownStyleProps: JSON.parse(wasm.snapshot_unknown_style_props()) };
await ask(session, {
stage: 'findings',