test(pagebreak): a texted marker's clean verdict can be a finding the gate drops - #201
Conversation
…, not clean `test/pagebreak-marker.test.ts` recorded a reason for #145's intermittency that is not the measured one. It asserts that a labelled page-break marker WITH text passes, and explains the silence as axe "declining to resolve the role at all". Measured here, through axe directly on the one rule: <p role="doc-pagebreak" aria-label="Page 5">5</p> 1 incomplete, 0 violations <p role="doc-pagebreak" id="page-5">5</p> 0 incomplete, 0 violations <p role="doc-pagebreak" aria-label="Page 5"></p> 0 incomplete, 1 violation `aria-prohibited-attr` returns `undefined` rather than `false` when the element has text of its own — the attribute is prohibited either way, but whether the reader loses anything depends on what that text says, which axe will not decide — so the finding lands in `incomplete`, and `runAxe` promotes only `duplicate-id-aria` out of `incomplete`. The six markers that passed were findings the GATE dropped, and the seventh had no text to demote it. That is a sharper answer to "why was it intermittent" than "six were clean", and it is the one a future reader of this file needs: a `[]` from the gate is not the same claim as "axe found nothing". The distinction is now measured rather than described, so the file cannot go stale on it in the other direction either: if the promotion list ever grows to include this rule, the new test fails and names the promotion list, instead of the failure reading as an axe regression somewhere else in the suite. The measurement runs `runOnly` by rule name rather than copying `runAxe`'s tag filter and rule tuning — a second copy of that configuration in a test would drift from the gate it exists to reason about. Nothing about the prescribed marker changes: `<hr role="doc-pagebreak" aria-label="Page 5" id="page-5">` is clean by this rule's own reckoning too, not merely unreported, and that row is pinned as well. Found while addressing the review of PR #148 (closed), whose third non-blocking note is this one: "'the same attribute beside text is clean' is true of Iris's gate, not of axe." Co-authored-by: bbertucc <46652+bbertucc@users.noreply.github.com> Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
There was a problem hiding this comment.
All six checks pass. One test file changed, no src/ behaviour, no workflow or agent-prompt changes. The four measured rows are correct and the point they support — a [] from the gate is not the claim "axe found nothing" — is a real sharpening. Two of the narrative claims wrapped around them are not.
Non-blocking notes
1. The "six markers were demoted" framing contradicts this file's own record of #145 — and its own middle row.
test/pagebreak-marker.test.ts:26-28:
the six markers that passed in #145's document were findings axe DEMOTED to
incomplete, not findings it never made.
and :190-192: "six markers were DEMOTED, and the seventh had no text to demote it."
The file's opening paragraph, unchanged by this PR (:1-4), records what those six were:
Six other markers in that document carried their number as text instead of a label and passed
A marker with no naming attribute is row 2 of the new measurement — <p role="doc-pagebreak" id="page-5">5</p> → {violations: 0, incomplete: 0} (:682) — and the new comment for that row says so itself: "there is nothing for this rule to judge and the silence really is silence."
So on this file's stated facts, #145's six markers produced no finding at all, not a demoted one. Only the label-and-text shape (:676) demotes, and that is not the shape the incident had. The correction is sound as a statement about aria-prohibited-attr; it is the attribution to those six markers (title included) that does not follow. Fix whichever of the two accounts of #145 is wrong.
Latent: comment text in a test file — no assertion is affected, nothing delivered changes.
2. The stated tripwire does not exist: this test cannot fail on a promotion-list change.
:190-193:
if the promotion list grows to include this rule, this test fails and says so, instead of the failure reading as an axe regression somewhere else in the suite
plus the assertion message at :678: "lint.ts's promotion list, or this test's argument, is out of date".
prohibitedAttr() (:543-566) calls w.axe.run directly. The promotion lives inside runAxe — src/pipeline/lint.ts:373, results.incomplete.filter((v) => v.id === "duplicate-id-aria") — which this helper never calls. Add aria-prohibited-attr to that filter and this test still measures {violations: 0, incomplete: 1} and still passes.
The test that would actually fail on that change is the existing gate test at :637, which asserts [] from rules() for the texted marker — and its failure message speaks about the prompt catching up, not about the promotion list. The intended signal is one test to the left of where the comment points, with the wrong explanation attached.
Latent: only misleads whoever reads the next failure.
Accessibility impact: none — test-only change; no assertion, prompt, or delivered document behaviour moves.
…s six Both notes from the review of #201 are right, and both are about what the comments claim rather than what the four rows measure. The attribution was wrong. #145's six passing markers carried their number as text INSTEAD of a label — this file's own opening paragraph says so — which is the middle row of the new measurement: no naming attribute, nothing for `aria-prohibited-attr` to judge, `{violations: 0, incomplete: 0}`. Their clean verdict was not a demoted finding. What the demotion explains is the labelled-AND-texted shape, which is the trap the test above it is about and the shape a model reaches for when told to keep the label and show the number. The correction to "axe has nothing to say about a texted marker" stands — it is true of one of the two shapes and false of the other — so the rows stay and the sentence around them says which is which. The stated tripwire did not exist either. `prohibitedAttr()` calls `axe.run` directly, so adding this rule to `runAxe`'s promotion list would leave every row here unchanged. The test that sees that change is the gate test above, whose `[]` would become a reported violation, and its message now names both ways it can start failing instead of only the prompt catching up. This test's own message speaks about axe's verdict, which is what it measures. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
Both notes fixed in 24f1b1c — you were right on both, and the second one changes what the test claims about itself. 1. The attribution. #145's six passing markers carried their number as text instead of a label, which is the middle row: no naming attribute, nothing for the rule to judge, 2. The tripwire. Correct — One note on your line numbers: they point past the end of the file (219 lines), so if you were reading a different revision the two findings still hold as stated — I checked both against the file at 7a5213d.
|
There was a problem hiding this comment.
Both notes from my review of 7a5213d are addressed in 24f1b1c, and the change is comment- and message-only — no assertions moved.
-
The "six markers were demoted" attribution is fixed.
test/pagebreak-marker.test.ts:28-30and:191-195now put #145's six markers in the middle row (no naming attribute → nothing for the rule to judge, silence really is silence) and reserve the demotion for the labelled-and-texted shape. That matches the file's own opening record at:2-4and the measured row at:212, and the commit title says the same thing. -
The mis-pointed tripwire is fixed, and correctly.
:198-201now states outright that this test is not the tripwire onrunAxe's promotion list, and the gate test's failure message at:171-173names both reasons a[]could break. I verified the second reason actually holds:aria-prohibited-attrdoes reach the gate throughrunAxe's tag filter (src/pipeline/lint.ts:359runswcag2a, and:90-97of this file asserts the empty marker surfaces asaria-prohibited-attr[serious]), so adding that id to theincompletepromotion atsrc/pipeline/lint.ts:373would indeed turn that[]into a reported violation and fail the gate test. The signal now points at the test that sees it.
No new findings. All six checks pass, and the only file this branch changes is the test.
For the record: the config.example.yaml, prd.md, src/pipeline/assembly.ts, src/pipeline/lint.ts, test/config-example.test.ts and test/document-lang.test.ts hunks in my review context are main's already-merged #198/#199 work — the diff was taken from the merge ref against an older merge base (9c499f0), not from this branch. git diff origin/main 24f1b1c confirms the branch does not carry them, so I have not re-reviewed them here.
Accessibility impact: none — a test-only change; no prompt, gate configuration or delivered document behaviour moves.
Addressing the review of PR #148 (closed by @bbertucc because it would have undone #145's fix — the shape it prescribed put
role="doc-pagebreak"on a<p>, where the role is dropped or its children are presentational either way). The shape argument is settled andmainhas it right. Its review left one note that still applies tomain, and this is it:That file never merged, but
test/pagebreak-marker.test.ts(which did, via #160) carries the same claim and explains the silence as axe "declining to resolve the role at all". Measured on this branch, through axe directly on that one rule:<p role="doc-pagebreak" aria-label="Page 5">5</p><p role="doc-pagebreak" id="page-5">5</p><p role="doc-pagebreak" aria-label="Page 5"></p><hr role="doc-pagebreak" aria-label="Page 5" id="page-5">aria-prohibited-attrreturnsundefinedrather thanfalsewhen the element has text of its own — the attribute is prohibited either way, but whether the reader loses anything depends on what that text says, which axe will not decide — so the finding lands inincomplete, andrunAxepromotes onlyduplicate-id-ariaout of it.So the six markers that passed in #145's document were findings the gate dropped, and the seventh had no text to demote it. That is a sharper answer to "why was it intermittent" than "six of them were clean", and it is the one a future reader of that file needs: a
[]from the gate is not the same claim as "axe found nothing".What changed
runOnlyby rule name rather than copyingrunAxe's tag filter and rule tuning. A second copy of that configuration in a test would drift from the gate it exists to reason about; one rule's own verdict needs none of it.Nothing about the prescribed marker changes, and no
src/behaviour changes. The<hr>row is pinned too, so "clean" there means clean by the rule's own reckoning and not merely unreported.Gates
npx tsc --noEmitcleannpm test— 776 tests, 776 pass, 0 fail (1 new)bash test/e2e.sh— ALL ENDPOINTS PASSED ✅Co-authored-by: bbertucc 46652+bbertucc@users.noreply.github.com
🤖 Generated with Claude Code