PATTERN Cited by 1 source
Exclusion list for known issues and out-of-scope rules¶
Context¶
A team runs automated checks (accessibility, linting, security scanning) against a production codebase. Some violations are:
- Known issues already ticketed — real bugs the team intends to fix, but hasn't yet. Re-reporting them on every run is noise.
- Out of scope — the check flags a pattern the team has deliberately chosen. The flag is correct according to the rule, but the rule doesn't apply to this codebase's design conventions.
Without a filter, both categories drown the genuinely actionable signal.
The pattern¶
Maintain a single exclusion list of selectors / file paths / rule identifiers, with entries categorised by which axis they fall on. Apply the exclusions before reporting.
// Single constant, two categories of entries:
constants.ACCESSIBILITY.AXE_EXCLUDED_SELECTORS = [
// Known issues – already ticketed (A11YAUTO-N), will fix, remove from list on close:
'#legacy-notification-bar', // A11YAUTO-42
'.old-modal .close-btn', // A11YAUTO-51
// Out of scope – rule does not apply to how Slack is designed:
'.tab__indicator', // aria-selected semantics don't match our tab impl
'.emoji-picker-button', // decorative, intentional
];
// Applied in the builder before .analyze():
constants.ACCESSIBILITY.AXE_EXCLUDED_SELECTORS.forEach(sel => {
axe.exclude(sel);
});
The two-axis structure¶
Both categories live on the same list (simpler operationally than two separate lists), but the categorisation drives different lifecycles:
| Axis | Lifecycle | Removal trigger |
|---|---|---|
| Known-issue | Fix-forward telemetry arc | Ticket closes, bug fixed, rule now passes |
| Out-of-scope | Structurally permanent | Design convention changes (rare) |
The distinction is documentation only — the engine treats every entry identically. But for list hygiene, knowing which is which determines who can remove an entry (only the a11y team should remove "rule doesn't apply" entries; any fixer can remove a "known-issue" entry when the underlying ticket closes).
Why a single list, not a config pane¶
- The rule engine only speaks selector-level exclusion.
Axe's
.exclude()takes a CSS selector; it doesn't distinguish "known" vs "won't-fix" — that metadata lives in code comments or an adjacent tracking system, not in the engine. - Single list = single review surface. Adding or removing an entry is a pull request; reviewers see the full list and can catch drift.
- Pair with the issue tracker. Known-issue entries should
reference the tracking ticket in a comment (
// A11YAUTO-42) so that closing the ticket is the trigger to remove the exclusion.
The new-bug triage loop¶
When an Axe violation surfaces and a ticket is created (see patterns/alert-channel-to-jira-auto-ticket-workflow), the ticket template instructs the triager to add the relevant selector to the exclusion list with the ticket ID in a comment:
*"Please fill out all of the necessary information listed here: https://jira.tinyspeck.com/browse/A11YAUTO-37.
Please add this locator to the list of known issues and include the new JIRA bug ticket in the comment."*
This closes the loop: the violation that just fired won't fire again until the fix lands (at which point the ticket closes and the exclusion entry is removed). Repeat-alerts during the fix-forward window are suppressed.
Failure modes¶
- Stale entries. Known-issue entries outlive their tickets if the removal-on-close discipline slips. Mitigation: periodic audit script that checks exclusion-list comments against ticket status.
- Over-broad selectors. An
.old-modalexclusion might shadow a new-modal bug. Mitigation: keep selectors as specific as possible; prefer.old-modal .close-btnover.old-modal. - Rule version drift. If the underlying rule engine is upgraded, the rule the exclusion was meant to suppress may have been renamed or deprecated. Mitigation: track engine version bumps; re-audit the exclusion list on each bump.
Generalisation¶
The pattern applies to any automated-check class:
- Linters — eslint-disable-next-line comments + .eslintrc
overridesblock. - Static analysers (SonarQube, CodeQL) — suppression files.
- Security scanners —
.snyk/.trivyignorefiles. - Performance budgets — per-page size-limit allow-lists.
Common shape: two semantically distinct buckets of suppressions (genuinely-we'll-fix vs doesn't-apply-by-design), flattened onto one operational list, reviewed via PR.
Seen in¶
- sources/2025-01-07-slack-automated-accessibility-testing-at-slack
— Slack's
constants.ACCESSIBILITY.AXE_EXCLUDED_SELECTORSpre-applied to everyAxeBuilderbefore.analyze(), with two explicit documented categories (ticketed known-issues + out-of-scope-by-design rules).
Related¶
- systems/axe-core / systems/axe-core-playwright — the
engine and binding whose
.exclude()API this pattern uses. - patterns/severity-gated-violation-reporting — composes with exclusion-list filtering; exclusion is pre-audit, severity is post-audit.
- patterns/alert-channel-to-jira-auto-ticket-workflow — the ticket-creation surface that feeds new entries into the exclusion list.