Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
68 changes: 68 additions & 0 deletions docs/design/0256-restore-a11y.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
# Design Doc: Restore a11y as `audit --check a11y`

> **Status:** Accepted
> **Date:** 2026-07-13
> **Author:** Claude (direct implementation, no worker — user directive)
> **Related issues:** #256
> **Related PRs:** (this PR)

---

## Problem

`a11y_engine.py` (WCAG 2.1 accessibility analysis: missing alt text, missing
form labels, ARIA issues, keyboard-nav gaps, non-semantic HTML, color contrast,
heading order, link text, focus management) is fully functional — verified 2
real findings on `tests/fixtures/sample.html` — but **orphaned**: its CLI
entry point (the old standalone `a11y` command) was deleted in the #195
umbrella consolidation, and `audit_accessibility()` is now reachable from no
command, MCP tool, or `--check` sub-mode.

This is the **exact same situation as `css-deep` (issue #251, PR #252)** and
`export-snapshot` (issue #218): a working engine with a dead entry point.

## Goal

`codelens audit <workspace> --check a11y` runs `audit_accessibility()` and
returns findings in the standard audit umbrella shape, with `--severity` and
`--category` passthrough.

## Changes

### New Files
- `scripts/commands/a11y.py` — thin wrapper over
`a11y_engine.audit_accessibility()`, mirroring `css_deep.py`'s structure.
No engine logic duplicated.

### Modified Files
- `scripts/commands/audit.py` — registered `a11y` in `_CHECKS`, added
namespace branch (severity + category passthrough), updated epilog.
- `tests/test_command_registry.py` — added `a11y` to the
implementation-module allowlist (it's imported by the audit umbrella,
not self-registering — same pattern as `css_deep`).

### Not Changed
- `a11y_engine.py` — the engine already works and needed no changes.

## Why a sub-check, not a restored top-level command

Same reasoning as #251: the #195 consolidation reduced 78 commands to 12
umbrellas. Re-adding `a11y` as a top-level command would violate that.
As `--check a11y` under `audit` — alongside dead-code / complexity / smell /
perf-hint / css — it fits the audit umbrella taxonomy and keeps the command
count at exactly 12.

## Testing

Verified end-to-end via CLI: `codelens audit tests --check a11y` → 2 real
findings (missing_label high + semantic_html low) from `tests/fixtures/sample.html`.
`--severity high` passthrough confirmed. `tests/test_command_registry.py`
2 passed.

## Alternatives Considered

- **Leave it dropped.** Rejected — the engine works, HTML/accessibility is in
scope (CodeLens covers HTML files), and the loss forces manual auditing.
- **Restore as a top-level `a11y` command.** Rejected — violates the
12-umbrella consolidation. Sub-check placement recovers the capability
without growing the command surface.
45 changes: 45 additions & 0 deletions scripts/commands/a11y.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
# @WHO: scripts/commands/a11y.py
# @WHAT: Accessibility (WCAG 2.1) audit command — thin wrapper over a11y_engine (issue #256)
# @PART: commands
# @ENTRY: execute()
"""a11y command — accessibility audit (issue #256 restoration).

Wraps ``a11y_engine.audit_accessibility()`` — detects missing alt text,
missing form labels, ARIA issues, keyboard-nav gaps, non-semantic HTML,
color-contrast, heading-order, link-text, and focus-management problems
(WCAG 2.1).

The engine was never deleted, but its CLI entry point (the old standalone
``a11y`` command) was dropped in the #195 umbrella consolidation, leaving
the working engine orphaned — the exact same situation as ``css-deep``
(issue #251) and ``export-snapshot`` (issue #218). This restores access as
``audit --check a11y`` — a sub-check under the audit umbrella, NOT a new
top-level command, so the 12-umbrella consolidation is preserved (command
count stays 12).
"""

from a11y_engine import audit_accessibility
from commands import register_command


def add_args(parser):
parser.add_argument("workspace", nargs="?", default=None,
help="Path to workspace root (auto-detected if omitted)")
parser.add_argument("--severity", choices=["high", "medium", "low"], default=None,
help="Filter by severity level")
parser.add_argument("--category", default=None,
help="Filter to one category: missing_alt, missing_label, "
"aria_issues, keyboard_nav, semantic_html, color_contrast, "
"heading_order, link_text, focus_management")


def execute(args, workspace):
return audit_accessibility(
workspace,
category=getattr(args, "category", None),
severity=getattr(args, "severity", None),
)

# Issue #256: registered as the `a11y` sub-check of the `audit` umbrella
# (see commands/audit.py), NOT a standalone command — command count stays
# 12. Imported by audit.py, not self-registering.
11 changes: 11 additions & 0 deletions scripts/commands/audit.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,10 @@
"module": "commands.css_deep",
"help": "Deep CSS analysis: unused vars, orphan keyframes, specificity wars, z-index abuse (issue #251)",
},
"a11y": {
"module": "commands.a11y",
"help": "Accessibility (WCAG 2.1): missing alt/labels, ARIA, keyboard-nav, semantic HTML (issue #256)",
},
}

ALL_CHECKS = list(_CHECKS.keys())
Expand All @@ -81,12 +85,15 @@
" side-effect Pure vs impure function analysis\n"
" css Deep CSS analysis: unused vars, orphan keyframes,\n"
" specificity wars, z-index abuse (issue #251)\n"
" a11y Accessibility (WCAG 2.1): missing alt/labels, ARIA,\n"
" keyboard-nav, semantic HTML (issue #256)\n"
"\n"
"Examples:\n"
" codelens audit . # all checks\n"
" codelens audit . --check dead-code # only dead-code\n"
" codelens audit . --check complexity,smell # pick subset\n"
" codelens audit . --check css # deep CSS analysis\n"
" codelens audit . --check a11y # accessibility audit\n"
)
parser.add_argument("workspace", nargs="?", default=None,
help="Path to workspace root (auto-detected if omitted)")
Expand Down Expand Up @@ -184,6 +191,10 @@
# cssdeep_engine accepts severity (high|medium|low) + single category
ns.severity = getattr(base_args, "severity", None)
ns.category = getattr(base_args, "category", None)
elif check_name == "a11y":
# a11y_engine accepts severity (high|medium|low) + single category
ns.severity = getattr(base_args, "severity", None)
ns.category = getattr(base_args, "category", None)

Check warning on line 197 in scripts/commands/audit.py

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Either merge this branch with the identical one on line "192" or change one of the implementations.

See more on https://sonarcloud.io/project/issues?id=Wolfvin_CodeLens&issues=AZ9aMxq3JgY_rLCyVCVn&open=AZ9aMxq3JgY_rLCyVCVn&pullRequest=260
return ns


Expand Down
2 changes: 1 addition & 1 deletion tests/test_command_registry.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ def test_every_command_module_registers():
# register_command() calls were removed because the CLI alias is gone,
# but the umbrella commands import them for --check sub-analyses.
_DEPRECATED_ALIAS_MODULES = {
"affected", "arch_metrics", "architecture", "binary_scan",
"a11y", "affected", "arch_metrics", "architecture", "binary_scan",
"circular", "complexity", "css_deep", "dashboard", "dataflow",
"dead_code", "dependents", "diagnostics", "diff", "env_check",
"export_snapshot", "git_status", "graph_schema", "import_snapshot",
Expand Down
Loading