Skip to content

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-modal exclusion might shadow a new-modal bug. Mitigation: keep selectors as specific as possible; prefer .old-modal .close-btn over .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 overrides block.
  • Static analysers (SonarQube, CodeQL) — suppression files.
  • Security scanners.snyk / .trivyignore files.
  • 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

Last updated · 470 distilled / 1,213 read