feat: image-alt-missing — opt-in, WCAG-aware, default off (MD045) - #12
feat: image-alt-missing — opt-in, WCAG-aware, default off (MD045)#12mehvetero wants to merge 2 commits into
Conversation
Images with empty alt are inaccessible to screen readers and invisible to search engines. The AST already carries the alt field on both image and imageReference nodes. Gated by opts.enableImageAlt (default false) — the caller (conductor / config) decides when to flip it on. In agent-authored or internal repos the value concentrates in public-facing content images; nudging every image costs tokens for little value. WCAG 1.1.1 treats empty alt as correct for purely decorative images. The finding message says so: "intentional for purely decorative images (WCAG 1.1.1); a content image needs a description (MD045)" — leaving the judgment to the reviewing agent, not claiming blanket harm. Fixture: planted  at L35 in defects-structure.md. Default run (enableImageAlt=false): image-alt-missing does NOT fire — existing defect-count expectations unchanged (only file-missing@35 added for the new image target). Five dedicated tests: - off by default (no finding even with empty alt) - fires when enabled - silent when alt is present - whitespace-only alt treated as empty - reference images covered SKILL.md: added image-alt-missing row noting opt-in default and the WCAG decorative-image exception. 142/142 pass, verify PASS, dist rebuilt.
Owner requirements from TheColliery#11 addenda: a junk floor and an excess ceiling, both language-fair. Floor (image-alt-too-short): Script-aware via grapheme clusters (Intl.Segmenter, Node 16+ built-in). CJK: 1 grapheme (a single character is a full word — "図"). Default: 3 graphemes ("img" is junk, "cat" is a word). Config-tunable via opts.minAltGraphemes (number or { cjk, default }). Ceiling (image-alt-too-long): 125 graphemes — WAI screen-reader concision convention. Alt is a label, not a caption; long descriptions belong in surrounding text or figcaption. Config-tunable via opts.maxAltGraphemes. Both language-fair: the floor uses script classification (CJK Unified / Ext A / Hiragana / Katakana ranges, >50% = CJK-heavy); the ceiling uses a single number (bounding excess is script-neutral). 7 new tests: Latin junk, CJK single char, Thai above floor, exact floor, above ceiling, exact ceiling, config-tunable overrides. 149/149 pass, verify PASS, dist rebuilt.
|
Thanks for the fast rework 🤝 — and the WCAG correction in the message text is exactly right; that wording is a keeper. We're not going to take the opt-in gate, though — and the reason is a design rule on our side rather than anything wrong with your implementation: a check that matters shouldn't be a switch, and a check that doesn't matter shouldn't exist. An on/off key skips everything or fills everything; the question "does THIS image matter?" is per-finding, not per-repo. CoalLedger already ships the instrument for exactly that split: CONFIRMED vs SUSPECTED. So What lands with credit to you: the engine-side design from #11/#12 (both The same principle is going into CoalMine at the file level ( I'll close this PR once our build lands on main (shortly), with the credit in the CHANGELOG. 🙏 |
|
Fair call — CONFIRMED vs SUSPECTED is the right instrument for this; a per-repo switch was the wrong one. Should have caught that the split already existed in the design before reaching for a gate. Lesson taken. |
…o toggle Adds an image-alt-missing check to the doc-structure AST engine: images and every imageReference form (full/collapsed/shortcut) with empty or whitespace-only alt. Detection is AST-native (a code span or fenced block showing "" as documentation stays silent) and applies main's ruling on PR #11/#12 (both not merged): decorative-vs-content intent is a human call, so the finding is SUSPECTED-only, ALWAYS — never a config toggle, never promoted to CONFIRMED. The finding shape gained a backward-compatible `suspected` field; every existing CONFIRMED check omits it, unchanged. Design credit to community contributor mehvetero, who raised the gap and the WCAG-aware message wording in #11/#12; the classification (SUSPECTED, no on/off) is this project's own. Closes the four surfaces PR #11's INSPECT found missed twice: the SKILL.md frontmatter description, its Checks table (also backfills the pre-existing missing doc-too-large row), the conductor's offer-line enumeration, and the CHANGELOG entry. Fixture + 7 new unit tests (silent/fires/whitespace/full reference form/collapsed-shortcut silence/code-span silence/backward-compat no-suspected-field); real negative case proven by sabotaging the predicate to always-fire in a scratch copy and confirming the shipped code stays silent where the sabotaged copy flags. Gates: build + 145/145 + VERIFY PASS; dogfood on every edited doc = 0 findings; standalone skill folder re-proven runnable outside the repo. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
… claim fixes image-alt-missing lands always-on, SUSPECTED-only (no config toggle) — the machine confirms the alt is absent; decorative-vs-content intent is the author's call. Engine design + WCAG message credit: mehvetero (PR #11/#12, not merged); the classification is ours. Also: the slugger anchor-claim wording corrected in all 5 homes (slug-based, first-come, text-independent) and doc-too-large backfilled into the Checks table. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
Landed on Closing as superseded by the on-main build. Thanks again for pushing on this one 🤝 |
Revised version of #11, addressing the owner's two requests from the revert.
Changes from #11
Default off. Gated by
opts.enableImageAlt(false by default). The caller — conductor, config, or a directcheckDocument()call — decides when to enable it. Same pattern as the docLeak canary gate.Message names the WCAG exception. "image has empty alt — intentional for purely decorative images (WCAG 1.1.1); a content image needs a description (MD045)." No blanket harm claim.
What stayed the same
The engine-side work from #11 is unchanged: both
imageandimageReferencenode types,node.alttrimmed, the fixture gate.Tests (5 new, all opt-in aware)
enableImageAlt: trueproducesimage-alt-missingis treated as missing![][ref]is also checkedFixture
defects-structure.md: plantedat L35. Default run (enableImageAlt=false): the defect-count expectation gains onlyfile-missing@35(the image target is absent);image-alt-missingdoes NOT fire — existing test unchanged.SKILL.md
Added
image-alt-missingrow noting opt-in default and the WCAG decorative-image exception.