Contact Us

Static Rules Validation

DOC-00082 reference implementor, developer

Static Rules Validation

The static rules framework (npm run lint:static-rules) performs build-time source analysis on content and template files. It complements lint:a11y:axe (which tests rendered pages post-build) by catching source-level issues — like missing alt text in markdown or broken internal links in docs/content — that rendered-page testing may not cover.

Who this is for

  • Implementors adding new validation rules to the framework
  • Developers understanding what the linter checks and how to suppress findings
  • AI agents working with content or template files that the linter scans

Running the linter

npm run lint:static-rules                      # Standard run — warnings exit 0
npm run lint:static-rules -- --strict          # Strict mode — warnings exit 1
npm run lint:static-rules -- --show-suppressed # Show suppressed findings

lint:static-rules runs as part of npm run validate (the site-level pipeline). The companion fixture test test:static-rules is a core unit test — run it via npm test (or npm run validate:all to run both pipelines together). See Validation Pipeline for the rationale behind the split.

Running the fixture tests

The fixture test suite verifies framework correctness against known-good and known-bad inputs:

npm run test:static-rules              # Quiet — only failures and warnings shown
npm run test:static-rules -- --verbose # Full output — all passing assertions shown

By default only test groups with a failure or warning print anything, keeping pipeline output clean. The summary always reports all three counts:

test:static-rules: 56 passed, 0 warned, 0 failed
  • passed — assertion held; no output in quiet mode
  • warned — soft assertion noted for visibility; does not fail the run. The fixture suite is expected to stay at 0 warned on a healthy run; non-zero warnings indicate the harness is surfacing advisory output rather than a failing assertion.
  • failed — assertion did not hold; exits non-zero

Current rules

alt/missing

Detects missing or empty alt text in content and template files.

Content files (.md, .mdx):

  • ![](url) — empty alt text
  • ![ ](url) — whitespace-only alt text
  • <img> without alt attribute

Astro templates (.astro):

  • <img> without alt attribute — flagged
  • <img alt=""> — explicit decorative image, passes
  • <img alt={expr}> — dynamic expression, passes

A component allow-list in static-rules.config.ts suppresses <img> findings within listed component source files (e.g., components that enforce alt text through their own prop contracts). The allow-list does not suppress findings at usage sites.

Detects broken path-based internal links in routed markdown/MDX content and selected .astro surfaces.

Content files (.md, .mdx):

  • Markdown links ([label](target)) are checked when the target is path-based
  • Raw HTML anchors (<a href="...">) are checked
  • Same-site relative links resolve relative to the authored document route
  • Query strings are ignored for path existence checks
  • Fragments validate only when the resolved target is content-derived

Astro templates (.astro):

  • Raw anchors with root-relative literal href values are checked
  • Link.astro href values are checked via the rule’s knownComponents config
  • Non-root-relative literal href values are warned as unsupported in .astro
  • Dynamic href={...} expressions warn only when they contain obvious internal literal path signals that cannot be resolved statically

Deferred / out of scope:

  • Same-origin absolute URLs
  • Button.astro and other non-listed components
  • Non-routed content collections such as fragments
  • Fragment validation for non-content-derived targets (warned as unsupported instead)

Severity and enforcement

Each finding has a severity: error, warning, or info. Enforcement is a separate concern:

  • Default mode: Only error-severity findings cause exit 1. Warnings are informational.
  • Strict mode (--strict or LINT_STRICT=1): Any finding at warning or above causes exit 1.
  • info-severity findings never affect exit code regardless of mode.

Configuration

Rules are registered in src/core/config/static-rules.config.ts. Each rule entry controls:

  • enabled — whether the rule runs
  • severity — overrides the rule’s default severity
  • options — rule-specific settings (e.g., the alt/missing component allow-list or the broken-link known-component list)

Disabling a rule or changing its severity requires only a config edit — no framework changes.

Exception system

Two mechanisms suppress findings: inline comments for content-level suppressions and a centralized manifest for system-level suppressions.

Inline suppression

Add an HTML comment on the line immediately before the finding (blank lines between comment and target are allowed for Prettier compatibility):

<!-- lint-ignore alt/missing — decorative separator image -->

![decorative](divider.png)

The reason after the em dash () is required. Omitting the reason is itself flagged as lint/missing-reason.

Inline suppression works in .md, .mdx, and .astro files.

Centralized manifest

src/core/config/static-rules-exceptions.ts supports system-level suppressions for cases where inline comments are impractical. Each entry specifies:

  • ruleId — which rule to suppress
  • file — target file path
  • context (optional) — narrows suppression to a specific finding
  • reason — why this suppression exists
  • approvedBy / date (optional) — audit trail

When context is provided, only the matching finding is suppressed. When omitted, all findings for that rule in that file are suppressed.

Viewing suppressed findings

Suppressed findings are excluded from the main output by default. The summary footer always shows the suppressed count. Use --show-suppressed to include suppressed findings in the output for audit purposes.

Adding a new rule

To add a rule to the framework:

  1. Create a rule file in scripts/lib/static-rules/rules/ implementing the StaticRule interface from types.ts
  2. Register the rule in scripts/lib/static-rules/rule-registry.ts
  3. Add a config entry in src/core/config/static-rules.config.ts
  4. Add fixture files in scripts/lib/static-rules/fixtures/ and assertion cases in test-static-rules.ts

Each rule declares which source types it needs (content-md, astro-template, route-manifest). The runner only loads file-backed sources and inventories required by enabled rules.

Relationship to other validation steps

  • lint:a11y:axe — tests rendered pages post-build against WCAG 2.2 AA. Catches runtime accessibility issues including contrast. Static rules catch source-level issues that rendered-page testing may miss.
  • lint:tokens — enforces the three-layer token contract (no raw colors in components). Static rules operate on different concerns (content quality, not token usage).
  • lint:content-ingestion — validates content frontmatter schemas. Static rules validate content body quality (alt text, broken internal links).
REQ-00083 implemented Missing alternative text shall generate warnings.
REQ-00129 implemented Missing alt text findings shall generate warnings rather than hard failures, with a documented exception path.
REQ-00175 implemented Broken internal links shall generate warnings by default; CI pipelines on production-targeted branches may be configured to treat them as hard failures.
REQ-00217 implemented Content validation shall use a pluggable static rules framework that supports rule registration via configuration, per-rule severity levels, and extensibility for new rules without modifying the framework core.
REQ-00218 implemented Static rule violations shall support a dual exception model: inline source comments for localized suppressions and a centralized TypeScript manifest for cross-cutting exceptions. Both mechanisms shall require a documented reason for traceability.
REQ-00219 implemented Broken link detection shall validate both route existence and fragment anchors against a pre-build route inventory that does not depend on astro:content availability, using slug generation compatible with Astro's github-slugger behavior.

Search

Search across pages and articles. Use arrow keys to navigate results.

Search across pages and articles.

Loading search...

Search is unavailable. Please try again later.

    No results for ""

    Try different keywords or fewer words.