Skip to content

Linting or Validation Rule

A declarative rule — instantiates Grammar-Guided Structure Recovery

A single declarative constraint that marks a recovered structure well-formed or ill-formed — the atomic, named, individually-toggleable unit the whole validation layer is built from.

A Linting or Validation Rule is the atom of the validation layer: one named, declarative constraint — a predicate over the recovered structure, plus a severity and a message — that judges whether a structure that already parsed should also stand. It is not the engine that runs rules and not a whole schema; it is the smallest independently-authored, independently-testable, independently-toggleable unit of validation. Its defining move is to make each well-formedness constraint a first-class, named object rather than logic buried inside the parser, so that validation is composed from small pieces that a team can add, test, configure, and switch off one at a time.

Example

A front-end team enforces the accessibility rule that every <img> must carry a non-empty alt attribute. Expressed as a validation rule, it is a predicate over the recovered DOM — for each img node, alt exists and is non-empty — with severity error, a message pointing at the offending element, and a fix hint ("add descriptive alt text"). It is named, say img-alt-required, so it can be cited in a report, toggled per project, or suppressed on a single line with a comment.

What makes the rule pull its weight is precisely what the grammar can't do: <img src="logo.png"> with no alt is perfectly well-formed HTML. It parses. Nothing about the grammar objects. The rule catches the structure that is grammatical yet wrong — and it does so as one small, shareable object that maps to a real standard (WCAG's non-text-content requirement) rather than as a special case wired into the parser.

How it works

A rule pairs a predicate over the structure (or a pattern that selects the nodes it applies to) with a severity, a message, and often a fix hint. Because it is declarative — it states what must hold, not how to check it — a shared engine can evaluate many such rules uniformly, and each rule can be named, versioned, unit-tested against known good and bad inputs, and enabled or disabled without touching the others. That atomic, declarative shape is the whole point: it separates defining a constraint from running it, so the constraint set evolves independently of the machinery that applies it.

Tuning parameters

  • Severity — error (block), warning (flag), or info. Sets how downstream treats a violation and how loudly it interrupts.
  • Scope / selector — which node types or patterns the rule applies to. Broad selectors catch more but risk false positives.
  • Parameterization — fixed vs. configurable (e.g. max nesting depth = N). Parameterized rules adapt to each project without being rewritten.
  • Autofix — whether the rule ships a mechanical repair or only reports the problem.
  • Override surface — how a violation may be suppressed: per-project config, per-file, or per-line.

When it helps, and when it misleads

Its strength is that it captures a constraint the grammar cannot or should not — the well-formed-but-wrong case — as a small, testable, shareable object, and that atomic granularity is what keeps a validation layer maintainable and each violation actionable. Its weaknesses follow from being a single blunt predicate. Too broad, it floods false positives until someone disables it globally, which defeats it silently; too narrow, it misses the variants it was meant to catch; and being node-local, it says nothing about combinations — two individually-legal things that conflict only together, which no single rule can see. The classic misuse is writing rules to match whatever the current codebase already does, so they never fire and merely ratify the status quo, instead of encoding what should be true. The discipline that guards against this is to make every rule earn its place with true- and false-positive tests, and to remove or repair rules that are chronically suppressed rather than leaving them as ambient noise.[1]

How it implements the components

The Linting or Validation Rule realizes exactly one component — it is that component, at its smallest granularity:

  • validation_rule — one named, declarative well-formedness constraint over the recovered structure, carrying its own severity and message: the atomic unit the validation layer composes.

It is only the unit. Running many rules at once to catch forbidden co-occurrences is the Invalid Combination Linter; checking a structure against a versioned semantic schema is Semantic Schema Validation; and defining what merely parses is the Grammar Rule Set.

Notes

A validation rule and a grammar rule sit in different layers and catch different failures. The grammar decides what parses; a validation rule catches structures that parse cleanly yet must not stand (a valid <img> with no alt text). Trying to fold every validation constraint back into the grammar is a common temptation and a mistake — it bloats the grammar, couples well-formedness to policy, and loses the per-rule toggling that makes a validation layer livable.

References

[1] The <img>/alt example maps to WCAG 1.1.1 (Non-text Content), which requires a text alternative for non-text content. It is a real, widely-adopted standard and a clean illustration of a constraint that is invisible to an HTML grammar yet essential to a correct document — exactly the gap a validation rule fills.