Skip to content

fix(gates 35,40,42,44): four a11y gates excused themselves from a repo full of markup, and three reported PASS over a crashed checker - #272

Merged
rubenvdlinde merged 2 commits into
mainfrom
fix/gates-35-40-42-44-na-blackout-and-crashed-checker
Aug 8, 2026
Merged

fix(gates 35,40,42,44): four a11y gates excused themselves from a repo full of markup, and three reported PASS over a crashed checker#272
rubenvdlinde merged 2 commits into
mainfrom
fix/gates-35-40-42-44-na-blackout-and-crashed-checker

Conversation

@rubenvdlinde

Copy link
Copy Markdown
Contributor

What this is

The acceptance test for gates 34–44 (the accessibility band), run at package sha cdfbd7a: plant one textbook true positive per gate in both a .vue app (opencatalogi, 93 .vue) and a PHP-template app (nldesign, zero .vue, one template) — the asymmetry that made #225/#261 possible — require the gate to FAIL and name the plant in both, then remove it and require the prior verdict back.

All 11 gates fired and named the plant in all 22 arms, and all 22 returned to baseline exactly. The _a11y_markup_files fix holds. Two defects survive that test, and neither is visible from it.

1. Four gates declare themselves NOT APPLICABLE over a repo full of markup

Gates 35, 40, 42, 44 still guarded on [ -d src ] while 34/36/37/39/43 had moved to _a11y_has_markup_dir, and the central applicability table listed the whole family under [ -d src ]. On a repo with a templates/ full of markup and no src/ — same run, same files:

gate-34/36/37/38/39/41/43 ran; four FAILED on the plants
gate-35/40/42/44 NOT APPLICABLE"this repo ships no frontend, so there is no .vue/.js/.ts source for this gate to inspect"

na is the one verdict that removes a gate from coverage accounting, and the reason is contradicted by the same run's own output three lines above it. The a11y family has read .php and .html since #261; the declaration never caught up.

No fleet app is templates-only today. nldesign is one rm away — its src/ holds a single manifest.json, the exact shape that made twelve gates pass over nothing in #225.

The four guards now call _a11y_has_markup_dir, and the applicability declaration calls the same function instead of restating it, so the two cannot drift again. No third scope definition was added (#247/#261).

2. A crashed checker reported PASS — gates 40, 42, 44 (#147 / #249)

With a python3 on PATH that exits 1 on every invocation, against opencatalogi:

[gate-40] form-label-association: PASS      <- 13 real findings one run earlier
[gate-42] link-text-quality:      PASS
[gate-44] autocomplete-attr:      PASS
[gate-34/37/38/39/41/43]:         SKIPPED (wiring)

gate-40 discarded its status with 2>/dev/null || true; 42 and 44 ran per-file inline heredocs and never had one. 42 and 44 move into check_link_text.py / check_autocomplete.py — one interpreter for the whole file set, findings on stdout, exit code as a status — and 40 gains the same guard.

Found while writing the tests

Measured after, not only before

Tightening a gate creates its own false positives, so the after-measure is the load-bearing one:

  • 15 repos, gates 34–44, before vs after: every verdict and every finding count identical, except the two new gate-44 true positives. The rewrites of 42 and 44 removed nothing.
  • opencatalogi and nldesign return to their exact pre-plant baselines.
  • ARM 4 was mutation-checked: reverting gate-42's guard to [ -d src ] turns it red with the finding it was written for. A check that cannot fail is not a check.

What the controls confirmed still holds

Verified independently, not assumed from the PR that claimed them: gate-34 both arms (#269 — the comment does not fire, window['confirm']() does) · gate-37 tabindex="-1" is not focusable (#251) · gate-38/41 a PHP comment mentioning <html> does not make a mount point a page root, and a commented-out <html lang> does not satisfy a real unlangged one (#247/#266) · gate-39 a bound :title / :aria-labelledby is a name, and a > inside an attribute does not hide the button (#259) · gate-40 a slotted NcCheckboxRadioSwitch is not flagged, a self-closing one is (#251) · gate-43 an aria-hidden spacer <th> is not flagged (the 8 false openconnector findings).

One finding NOT fixed here, reported instead

gate-40 flags <input type="file" aria-hidden="true" tabindex="-1"> — the exact element gate-37's #222 fix declared canonical and correct. Two gates in the same band contradict each other, and aria-label on an aria-hidden element is inert, so the finding cannot be closed honestly. Zero fleet occurrences today (nldesign's three are display:none, not aria-hidden), and excluding display:none would be blinding — a style can be toggled by JS. Filing rather than guessing.

Tests

  • test_check_link_text.py, test_check_autocomplete.py — 32 assertions. Every relaxation ships with the true positive it must not swallow; each comment/script exclusion ships with its positive control; each file ends with the whole pre-fix checker replayed as the mutant, asserting it answers differently on every fixture.
  • test_gate_a11y_helper_wiring.sh gains gates 39, 40, 42, 44 — 39 was wired correctly but never listed, so nothing held it to that. 70 assertions.
  • test_gate_a11y_markup_scope.sh gains ARM 4, the templates-only repo.
  • Full discovered suite 49 passed / 0 failed (2 pre-existing quarantines); test-hydra-gates-bin.sh 59 passed / 0 failed.

🤖 Generated with Claude Code

…o full of markup, and three reported PASS over a crashed checker

Measured at package sha cdfbd7a against opencatalogi (93 .vue) and nldesign
(zero .vue, one PHP template), one textbook true positive planted per gate in
BOTH — the asymmetry that made #225/#261 possible.

All 11 gates in the 34-44 band fired and named the plant in both arms, and all
returned to their exact prior verdict on removal. Two defects survive that.

1. FOUR GATES GO `na` ON A TEMPLATES-ONLY REPO
   Gates 35, 40, 42 and 44 still guarded on `[ -d src ]` while 34/36/37/39/43
   had moved to `_a11y_has_markup_dir`, and the central applicability table
   listed the whole family under `[ -d src ]`. On a repo with a `templates/`
   full of markup and no `src/`, same run, same files:

     gate-34/36/37/38/39/41/43   ran; four of them FAILED on the plants
     gate-35/40/42/44            NOT APPLICABLE — "this repo ships no
                                 frontend, so there is no .vue/.js/.ts
                                 source for this gate to inspect"

   `na` is the one verdict that removes a gate from coverage accounting, and
   the reason was contradicted by the same run's own output three lines above
   it. No fleet app is templates-only today; nldesign is one `rm` away, since
   its `src/` holds a single `manifest.json` — the exact shape that made
   twelve gates pass over nothing in #225.

   The guards now call `_a11y_has_markup_dir`, and the applicability
   declaration calls THE SAME FUNCTION rather than restating it, so the two
   cannot drift again. No third scope definition was added.

2. A CRASHED CHECKER REPORTED PASS (#147 / #249) — gates 40, 42, 44
   With a `python3` on PATH that exits 1 on every call, run against
   opencatalogi:

     gate-40 PASS  gate-42 PASS  gate-44 PASS        <- the three inline ones
     gate-34/37/38/39/41/43 SKIPPED (wiring)         <- the six behind a helper

   gate-40 printed PASS over the 13 real findings it had reported one run
   earlier. gate-40 discarded its status with `2>/dev/null || true`; 42 and 44
   ran per-file inline heredocs and never had one. 42 and 44 move to
   scripts/lib/check_link_text.py and scripts/lib/check_autocomplete.py — one
   interpreter for the whole file set, findings on stdout, exit code as a
   status — and 40 gains the same return-code guard.

FOUND WHILE WRITING THE TESTS

  * gate-44 judged an input on the FIRST of name/id/v-model and stopped, so
    `<input id="e" type="text" name="email">` — the plainest textbook case
    this gate has — passed. Fleet effect, measured across 15 repos:
    openregister 0 -> 1 (an OpenAI Organization ID field), pipelinq 4 -> 5 (a
    "Colleague email" field). Both genuine, nothing lost.
  * gates 35, 36 and 44 read attribute values out of DOUBLE QUOTES ONLY.
    `tabindex='5'`, `alt=''` and `name='telephone'` render identically and
    reported PASS in both arms. Zero occurrences in the fleet today, which is
    why they could sit there indefinitely.
  * `[^>]*` in gates 42 and 44: a `>` inside an attribute value is not the end
    of a tag — the parse that hid 19 buttons from gate-39 (#259, #198, #236).
  * gates 42 and 44 scanned RAW text, so a commented-out `<a>click here</a>`
    or `<input name="email">` counted. That is gate-64's defect (#184), the
    one gate-38 (#247) and gate-41 (#266) each shipped a fix for.

MEASURED AFTER, NOT ONLY BEFORE
  * 15 repos, gates 34-44, before vs after: every verdict and every finding
    count identical except the two new gate-44 true positives above. The
    rewrites of 42 and 44 removed nothing.
  * opencatalogi and nldesign return to their exact pre-plant baselines.
  * ARM 4 of test_gate_a11y_markup_scope.sh was mutation-checked: reverting
    gate-42's guard to `[ -d src ]` turns it red with the finding it was
    written for.

TESTS
  * scripts/lib/test_check_link_text.py, test_check_autocomplete.py — 32
    assertions; every relaxation ships with the true positive it must not
    swallow, comment/script exclusions ship with their positive control, and
    each ends with the whole PRE-FIX checker replayed as the mutant, asserting
    it answers DIFFERENTLY on every fixture.
  * test_gate_a11y_helper_wiring.sh gains gates 39, 40, 42, 44 (39 was wired
    correctly but never listed, so nothing held it to that) — 70 assertions.
  * test_gate_a11y_markup_scope.sh gains ARM 4, the templates-only repo.
  * Full discovered suite: 49 passed, 0 failed, 2 pre-existing quarantines.
    tests/test-hydra-gates-bin.sh: 59 passed, 0 failed.
`case " 38 45 " in *" ${_g} "*)` matches a constant against a pattern
built from the variable, which is the comparison written backwards. It
happened to work, and ShellCheck is right that it reads as a mistake.
Verified with shellcheck 0.10.0 at full severity: clean.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant