Skip to content

feat: image-alt-missing — opt-in, WCAG-aware, default off (MD045) - #12

Closed
mehvetero wants to merge 2 commits into
TheColliery:mainfrom
mehvetero:feat/image-alt-v2
Closed

feat: image-alt-missing — opt-in, WCAG-aware, default off (MD045)#12
mehvetero wants to merge 2 commits into
TheColliery:mainfrom
mehvetero:feat/image-alt-v2

Conversation

@mehvetero

Copy link
Copy Markdown
Member

Revised version of #11, addressing the owner's two requests from the revert.

Changes from #11

  1. Default off. Gated by opts.enableImageAlt (false by default). The caller — conductor, config, or a direct checkDocument() call — decides when to enable it. Same pattern as the docLeak canary gate.

  2. 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 image and imageReference node types, node.alt trimmed, the fixture gate.

Tests (5 new, all opt-in aware)

test what it pins
off by default no finding even with an empty-alt image
fires when enabled enableImageAlt: true produces image-alt-missing
silent with alt image with real alt text passes
whitespace = empty ![ ](img.png) is treated as missing
reference images ![][ref] is also checked

Fixture

defects-structure.md: planted ![](./img/no-alt.png) at L35. Default run (enableImageAlt=false): the defect-count expectation gains only file-missing@35 (the image target is absent); image-alt-missing does NOT fire — existing test unchanged.

SKILL.md

Added image-alt-missing row noting opt-in default and the WCAG decorative-image exception.

142/142 pass, verify PASS, dist rebuilt

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 ![](./img/no-alt.png) 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.
@HetCreep

Copy link
Copy Markdown
Member

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 image-alt-missing lands always-on, classified SUSPECTED (same pattern as doc-leak): the engine mechanically confirms the alt is absent — that half is certain — while decorative-vs-content is intent, which only the author can judge. A decorative empty alt gets dismissed in review; a content image missing its description is the finding that matters. Skip the unimportant, fill the important — no config key.

What lands with credit to you: the engine-side design from #11/#12 (both image/imageReference node types, trimmed alt, all reference forms) and your corrected WCAG message. The release-side surfaces (CHANGELOG, conductor offer line, SKILL table) we always write ourselves — no action needed there.

The same principle is going into CoalMine at the file level (scanExcludePaths: declared lab/scratch paths get skipped, the shipped tree is never skippable). Your PR pushed us to name the rule properly — that's worth more than the diff itself.

I'll close this PR once our build lands on main (shortly), with the credit in the CHANGELOG. 🙏

@mehvetero

Copy link
Copy Markdown
Member Author

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.

HetCreep added a commit that referenced this pull request Jul 30, 2026
…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 "![](x)" 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>
HetCreep added a commit that referenced this pull request Jul 30, 2026
… 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>
@HetCreep

Copy link
Copy Markdown
Member

Landed on main and tagged v0.3.0-beta.2: image-alt-missing ships always-on as SUSPECTED-only, with your engine design (both node types, trimmed alt, all reference forms) and your WCAG message wording — credited in the CHANGELOG. Re-checking this area also surfaced and fixed a real slugger-claim bug in five doc homes, so the PR paid for itself twice.

Closing as superseded by the on-main build. Thanks again for pushing on this one 🤝

@HetCreep HetCreep closed this Jul 30, 2026
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.

2 participants