On this page

Build verification is the core of Wire's Content Build System. Wire does not trust what it builds. Two systems enforce quality: BUILD REFUSED gates block the build before rendering starts, and lint rules check the finished HTML after rendering. Both produce specific, actionable messages. Pages that fail do not ship silently.

This is the final layer of Wire's quality system. The content quality layers work on markdown before and during writing. Build verification works on the finished HTML after rendering. It catches problems that only become visible in the final output: broken links between rendered pages, missing meta tags that templates should have inserted, images without dimensions, structured data with invalid JSON.

$100K+Spent on manual recovery, still failed
12K→3KArticles pruned trying to fix quality
Page 1→3From one cannibalization mistake
1.8sWire checks 91 rules, zero cost

BUILD REFUSED Gates

Gates run before or during the build. When a gate fails, Wire stops and tells you exactly what is wrong and how to fix it. Gates are checked in order. Fix the first failure, rebuild, and the next gate runs.

Configuration Gates

These check wire.yml before anything else happens.

Gate Trigger Fix
Missing wire.yml No wire.yml in site directory Create wire.yml or run python -m wire.chief init
Missing required fields site_name, site_url, lang (or languages), or nav missing Add the missing fields. These are strategic decisions. Wire does not guess.
Missing sidebar_cta extra.wire.sidebar_cta missing or incomplete Add sidebar_cta: with title, description, link, link_text under extra.wire:
Missing article_cta extra.wire.article_cta missing or incomplete Add article_cta: with title, description, link, link_text under extra.wire:
Missing logo logo key missing from wire.yml Add logo: assets/logo.svg or logo: false for text-only
Missing favicon favicon key missing from wire.yml Add favicon: assets/favicon.svg or favicon: false for none
Logo/favicon not found Path in wire.yml points to nonexistent file Create the file or fix the path
Missing labels extra.wire.labels missing on_this_page, related_articles, or read_more Add all three labels under extra.wire.labels:
Missing discovery labels Non-English site with discovery steps but no discovery_start or discovery_read Add both labels under extra.wire.labels:
Invalid og_image og_image in wire.yml is a URL or missing file Use a local file path relative to docs_dir
Invalid BotPoison key extra.wire.form_botpoison starts with sk_ (secret key) Replace with your public key (pk_...). Secret keys must never be client-side.
Invalid footer.links footer.links is a flat list instead of grouped dict Restructure as footer: links: Company: - About: /about/

These validate the nav structure in wire.yml. Checked after offerings resolution.

Gate Trigger Fix
Missing nav No nav: key in wire.yml Add nav: with your site structure. No auto-discovery.
Nav orphans Pages exist but are not reachable via nav: Add pages to nav: or exclude with exclude: patterns
Legal pages in nav Legal pages (imprint, privacy, contact, etc.) in nav: Remove from nav:. Wire auto-adds them via footer.legal.
Alphabetical nav order 3+ sections in alphabetical order Reorder by importance. Alphabetical looks uncurated. Skipped when offerings configured.
Duplicate URLs Same URL in offerings and content nav A page cannot be both. Remove from one.
Missing header.offerings Site has topics but no header.offerings Add header: offerings: <topic-name> or false to disable
Too many offerings header.offerings list has more than 4 items Consolidate. The header layout breaks with more than 4.
Missing header.cta Site has topics but no header.cta Add header: cta: label: ..., url: ... or false to disable
CTA URL in nav CTA URL duplicates a content nav URL CTA is its own nav element. Remove from content nav.
CTA page layout CTA page missing layout: landing Add layout: landing to the CTA page's frontmatter
Offerings page layout Offerings child page missing valid layout Add layout: landing, page, or article to frontmatter

Frontmatter Gates

These validate every markdown file's YAML frontmatter. See Data Model: Frontmatter Contract for the full schema.

Gate Trigger Fix
Missing title or description Page missing title or description Add both to frontmatter. Every page needs them.
Unknown keys Frontmatter key not in known keys list Remove the key or use a known key.
redirect: in frontmatter Page uses redirect: key Remove it. Add redirect to wire.yml under redirects: instead.
Missing created Content page (topic + slug) without created date Add created: YYYY-MM-DD. Run python -m wire.migrate to backfill from git.
Invalid date format created not in YYYY-MM-DD format Fix to YYYY-MM-DD.
Placeholder date created on January 1st (e.g. 2025-01-01) Run python -m wire.migrate to backfill real dates from git history.
Future date created or date is in the future Set to today or an actual past date.
Missing short_title Topic page without short_title Add short_title: (max 20 chars). Used for nav tabs and sidebar.
Invalid short_title Ends with preposition (for, and, with) or punctuation Complete the label. "Solutions for" is incomplete; "Solutions" works.
Missing layout Parent page (index.md with child dirs) without layout Add layout: page or another valid layout.
Missing author Content page on site with authors/ directory Add author: <slug> matching a page in docs/authors/.
Invalid author/reviewer Author or reviewer slug not found in docs/authors/ Fix the slug. Error message lists available slugs.

Content Structure Gates

These catch structural problems in your docs/ directory.

Gate Trigger Fix
Numbered slugs Directory name starts with digits (e.g. 2-foo/) Rename to descriptive slug. Date slugs (YYYY-MM-DD-*) are allowed.
Empty topic Topic directory with only index.md, no content pages Add content pages or remove the empty directory.
Pending news Unprocessed YYYY-MM-DD.md files in content dirs Run python -m wire.chief refine <topic> to integrate them.
Migration artifacts .py files at site root (except setup.py, conftest.py) Delete migration scripts after migration is complete.
MkDocs syntax Files use {.class}, <div markdown>, !!!, or === "Tab" Replace: {.class} with :::cta, !!! with > blockquote, === with ## headings.
Content cannibalization Hard overlaps detected (ratio > 0.4, skew > 0.7) Run python -m wire.chief audit <topic> then deduplicate <topic>.
Root page missing layout Homepage index.md without explicit layout Add layout: landing or layout: home to frontmatter.

Shortcode Gates

These validate Wire shortcode syntax in markdown.

Gate Trigger Fix
Cards list syntax :::cards uses - [Title](/url/) instead of headings Use ### Title with a CTA link below each heading.
Links in shortcode headings ### [Link](/url/) inside cards, tabs, faq, or pricing Use plain text headings. Links belong in the body.
Banner value too long :::banner value exceeds 6 characters Shorten to 6 characters or fewer.
Unsupported section variant :::section dark or :::section accent Use :::section (light) or custom CSS for dark styling.
Missing !makeIMG images !makeIMG[slug] references but image file missing Run python -m wire.chief images to generate them.

Redirect Gates

Gate Trigger Fix
Redirect source conflict Redirect source path also has a docs/ file Delete the docs file or remove the redirect entry.
Self-redirect Redirect source equals destination Remove the circular redirect from wire.yml.
Query params in redirect Redirect source contains ? or = Redirects must be clean paths, not query strings.

GSC and SEO Gates

Gate Trigger Fix
GSC coverage gap GSC URLs with impressions have no page and no redirect Add redirects in wire.yml redirects: or run python -m wire.chief data to refresh.
Stale per-language databases Old .wire/{lang}/gsc.db files from pre-migration Delete old DBs, run python -m wire.chief data. Wire uses single .wire/gsc.db now.

Multi-Language Gates

Gate Trigger Fix
Language dirs without config docs/ has 2+ language-code dirs but no languages: in wire.yml Configure languages: in wire.yml.
Language asymmetry Language has < 20% of default language's page count Translate more pages or remove the language. Ghost-town translations hurt.
Orphan language dirs Language directory exists but not in wire.yml config Delete the directory or add it to languages: config.
Orphan language config Language in wire.yml but directory does not exist Create the directory or remove from config.
Duplicate titles Same title in multiple language directories Translate the title. Identical titles = untranslated content.
Untranslated bodies Page content in wrong language (stopword detection, min 50 words) Translate the content. Supported: de, en, es, fr.
Unsupported language Language has no stopword set for Gate 14 detection Add stopwords to _STOPWORDS in build.py.
Invalid alternate targets alternate: frontmatter points to nonexistent pages Ensure all alternate targets exist.
Non-reciprocal alternates A maps to B but B does not map back to A Add reciprocal alternate: to both pages.

Content Command Gates

These block content-modifying CLI commands (not the build itself).

Gate Trigger Fix
Uncommitted changes docs/ has uncommitted git changes git add docs/ && git commit before running content commands.
Missing styleguide No docs/_styleguide.md when running content generation Run python -m wire.chief init to generate a starter. See Writing for Wire.
translate without --lang translate command missing --lang flag Add --lang <code>, e.g. --lang de.
Styleguide language mismatch Styleguide language differs from --lang target Translate _styleguide.md to target language first.
relink without --lang relink command missing --lang flag Add --lang <code>.
migrate-lang: no wire.yml Running outside a Wire site Run from a directory with wire.yml.
migrate-lang: already multi Site already has languages: configured Only for converting single-language sites.
migrate-lang: missing docs docs_dir in wire.yml does not exist Create the directory or fix the path.
migrate-lang: lang conflict --from language also in --add list Remove the duplicate.

How Lint Works

After wire.build renders your site to HTML, run the verification:

python -m wire.lint --site /path/to/site

Wire parses every HTML file, builds a cross-page index (for duplicate detection and orphan checks), and runs all 91 rules. The output lists every failure with the rule ID, the affected URL, and what to fix.

You can also run specific checks:

python -m wire.lint --site /path/to/site --rules RULE-01,RULE-05,RULE-33

On a typical 200-page site, the full scan takes seconds. Zero API calls, zero cost.

Build Exit Codes

Level Example Site built? Exit code Can deploy?
BUILD REFUSED Missing frontmatter, invalid cards syntax, bad nav config No, build aborts 1 No
Lint errors RULE-05 title too long, RULE-33 broken link Yes, full site in site/ 1 No
Lint warnings RULE-52 dashes, RULE-42 image dimensions Yes, full site in site/ 0 Yes

Use --strict to treat warnings as errors (exit 1 even when the site was fully built).

Fixing Lint Issues

Fix free issues first, then use AI for the rest:

python -m wire.build                    # See all issues (also saved to .wire/build-errors.txt)
python -m wire.chief sanitize           # Fix broken links for free (no AI)
python -m wire.chief lint-fix           # Redirect links free, then AI for remaining
python -m wire.build                    # Rebuild to verify

lint-fix runs two steps: (1) redirect link rewriting (free, mechanical), then (2) AI content fixing via Haiku (low token usage, sequential per page).

Common Lint Patterns

  • Page titles: use : not - as separator. Dashes in titles trigger RULE-52 when rendered as link text in sidebar and related articles.
  • Math formulas: no spaces around operators inside backticks. (1-CTR) not (1 - CTR).
  • Card shortcodes: each card must link to a different page. Same URL triggers RULE-81.
  • PDF links: add (PDF) to anchor text: [Title (PDF)](url.pdf).
  • Headings: use parentheses not dashes. ## Module (Description) not ## Module - Description.
  • RULE-52 fires after fix: the dash is in a page title that appears as link text on other pages. Fix the title in frontmatter.
  • Lint count goes UP after fixing: anchor fragment links like /page/#section can introduce RULE-33. Point to the page URL without fragments.
  • Fixed 30 pages, count barely changed: many RULE-52 hits come from one title. Fix the title once, all hits disappear.

Per-Page Lint Suppression

If a rule fires but the content is genuinely correct, suppress per-page with _overruled: in frontmatter:

---
title: My Page
_overruled:
  - RULE-85
---

The warning still appears in output but does not block. Error-level rules (RULE-22, RULE-33, RULE-36, RULE-37, RULE-41, RULE-48, RULE-51) cannot be overruled.

What Wire Checks

The 91 rules fall into 16 categories. Each rule exists because independent research, not Google's public statements, shows it affects search performance or site health.

Page Structure (16 rules)

These checks verify that every page has the HTML elements search engines need to understand and rank it.

Rule What it catches Why it matters
RULE-01 Missing or empty H1 SearchPilot A/B test: pages with keyword-aligned H1 saw 28% more traffic. The H1 tells Google the page's primary topic
RULE-02 Multiple H1 tags on one page The 2024 Google API leak confirmed entity extraction uses heading structure. Multiple H1s dilute the primary topic signal
RULE-03 Skipped heading levels (H1 then H3, no H2) Heading hierarchy feeds semantic structure signals. Skipped levels break the logical outline Google uses for entity extraction
RULE-04 Target search query missing from H1 or title Zyppy's 81K title study: Google rewrites titles 61.6% of the time when they do not match the page's primary heading
RULE-05 Title tag missing, empty, too short (<20), too long (>65), or duplicate Backlinko's analysis of 11.8M search results: title tag optimization correlates with higher rankings. Google truncates titles over ~60 characters in search results
RULE-06 Multiple <title> tags Browsers use the first one. Search engines may use either. Remove the duplicate
RULE-07 Meta description missing or over 158 characters Google truncates long descriptions in search results. A missing description means Google writes one for you, and it rarely matches your intent
RULE-08 Thin content (fewer than 200 words) Multiple case studies show thin pages hurt when they cannibalize stronger siblings. 201Creative deleted thin ecommerce pages and saw +867% traffic. This rule flags stubs that need expanding
RULE-09 Missing Open Graph tags (og:title, og:description) These control how your pages appear when shared on LinkedIn, Twitter, Slack, and other platforms. Missing tags mean the platform guesses, usually poorly
RULE-65 Missing og:image tag Social shares without an image show a blank preview. Wire resolves og:image from frontmatter image:, first body <img>, or theme.og_image in wire.yml. Set a site-wide fallback to cover text-only pages
RULE-66 <form> without submission handler A form without action= and no form_id configured renders but cannot submit. Either add form_id to wire.yml for Formspark handling, or add action= to point the form at your own endpoint
RULE-10 Missing viewport meta tag Without it, mobile browsers render pages at desktop width and scale down. Google's mobile-first indexing penalizes pages that are not mobile-friendly
RULE-11 No character encoding in first 1024 bytes Browsers need to know the encoding before parsing. Missing charset can cause garbled text, especially for non-English content
RULE-103 Root index page without explicit layout Wire needs to know how to render the homepage. Add layout: landing or layout: home to frontmatter
RULE-104 Landing page with no Wire components A landing page without :::stats, :::cards, or other components is just a regular page. Use components or switch to a different layout
RULE-107 Landing page hero text exceeds 40 words Long hero text before the first component reduces visual impact. Move details below the hero

Canonicalization (4 rules)

Canonical tags tell search engines which version of a page is the "real" one. Getting this wrong splits your ranking signals across multiple URLs.

Rule What it catches Why it matters
RULE-12 Missing canonical tag Without a canonical tag, Google decides which URL to index. If your page is accessible at multiple URLs (with/without trailing slash, with/without www), Google may split your ranking signals
RULE-13 Multiple canonical tags When Google finds two canonical tags pointing to different URLs, it ignores both. Your page has no canonical signal at all
RULE-15 Canonical URL not found in sitemap If the canonical URL and sitemap URL disagree, Google sees conflicting signals about which page matters. Both should point to the same URL
RULE-16 Duplicate H1 across different pages Two pages with the same H1 create a cannibalization signal. Google cannot tell which page should rank for that heading's topic. Differentiate or consolidate

URL Structure (6 rules)

Clean URLs help search engines and users. These rules enforce the URL patterns that every major SEO study recommends.

Rule What it catches Why it matters
RULE-17 Uppercase characters in URL path URLs are case-sensitive on most servers. /About/ and /about/ are different pages. Uppercase creates duplicate content risk
RULE-18 Special characters in URL path Spaces, brackets, and non-ASCII characters in URLs cause encoding issues. Some crawlers fail to follow these links
RULE-19 Underscores in URL path Google treats hyphens as word separators but underscores as joiners. content-strategy reads as two words; content_strategy reads as one
RULE-20 URL longer than 115 characters Long URLs get truncated in search results and are harder to share. Backlinko found shorter URLs correlate with higher rankings
RULE-21 Query parameters on page URL Parameters like ?utm_source=... on canonical pages create duplicate content. Strip parameters from canonical URLs
RULE-22 Internal links missing trailing slash [ERROR] Inconsistent trailing slashes create duplicate URLs. If /guides/ and /guides both work, Google may index both and split ranking signals

Sitemap Integrity (4 rules)

Your sitemap tells Google which pages exist and matter. These rules catch contradictions between your sitemap and your actual pages.

Rule What it catches Why it matters
RULE-23 No sitemap.xml at site root Without a sitemap, Google relies entirely on crawling to discover pages. Large sites can have pages that Google never finds
RULE-24 Published pages missing from sitemap If a page exists but is not in the sitemap, Google may deprioritize crawling it. Every indexable page should appear
RULE-25 noindex pages listed in sitemap Contradictory signals: the sitemap says "index this" while the page says "do not index." Google sees confusion and may ignore both directives
RULE-26 robots.txt-blocked pages listed in sitemap The sitemap includes a page that robots.txt blocks from crawling. Google cannot crawl it but sees it in the sitemap, another contradictory signal

Crawl Access (4 rules)

robots.txt controls which pages search engines can access. Mistakes here can accidentally hide your entire site.

Rule What it catches Why it matters
RULE-28 No robots.txt file Every site should have one. Without it, crawlers assume everything is allowed, including admin pages, staging content, and asset directories you may not want indexed
RULE-29 robots.txt blocking CSS or JavaScript files Google needs CSS and JS to render your pages. Blocking them means Google sees your site without styling and cannot evaluate mobile-friendliness, layout, or dynamic content
RULE-30 No Sitemap directive in robots.txt The Sitemap line in robots.txt is how crawlers discover your sitemap. Without it, they have to guess the location
RULE-31 robots.txt blocking all crawlers (Disallow: /) This makes your entire site invisible to search engines. Usually a leftover from staging that was never removed

Internal links distribute ranking power across your site. Broken links waste it. Orphan pages never receive it. External links and special link types (tel:, PDF) need correct formatting to work across devices and to set user expectations.

Rule What it catches Why it matters
RULE-33 Broken internal links (target page does not exist) The 2024 Google API leak lists badBacklinks as a negative ranking signal. Broken internal links waste crawl budget and pass zero ranking power. Wire checks every link against the rendered site directory
RULE-34 Internal links with empty anchor text Google uses anchor text to understand what the target page is about. Empty anchor text wastes this signal. Screen readers also cannot describe the link to visually impaired users
RULE-35 Orphan pages (zero inbound internal links) SearchPilot's controlled experiment showed that adding internal links to orphan pages produces a statistically significant traffic increase. Pages with no inbound links are invisible to navigation and receive minimal crawl attention
RULE-36 Links pointing to redirect stubs instead of final URLs [ERROR] Linking to a redirect wastes a hop and passes less ranking power. Link to the final destination
RULE-66 Form without submission handler A <form> without action= and no AJAX handler renders but cannot submit. Add action= or JavaScript handling
RULE-69 Cross-language internal links A German page linking to /en/ content confuses language signals. Keep internal links within the same language directory
RULE-70 Absolute self-links to own domain Using https://yourdomain.com/path/ instead of /path/ creates unnecessary coupling. Use relative paths
RULE-71 Articles below minimum link thresholds Articles need sibling links, a topic index link, and at least one external link for source credibility
RULE-72 Generic anchor text like "click here" or "read more" Google uses anchor text to understand the target page. Generic text wastes this signal
RULE-78 Pages with zero outbound internal links Dead-end pages trap visitors and waste crawl budget. Add at least one internal link
RULE-81 Same URL linked with different anchor text Conflicting anchor text for the same target confuses Google about what that page is about
RULE-92 Article with only one outbound internal link Weakly integrated content. Add more internal links to related pages
RULE-101 tel: links not in E.164 format Phone links should use international format (+49...) for consistent handling across devices
RULE-102 Links to PDF files without file type indicator Users should know they are downloading a PDF before they click

Security (1 rule)

Rule What it catches Why it matters
RULE-37 HTTP resources loaded on HTTPS pages (mixed content) Browsers block or warn about mixed content. Images loaded over HTTP on an HTTPS page may not display. Scripts may be blocked entirely. Google flags mixed content as a security issue

Images (4 rules)

Rule What it catches Why it matters
RULE-40 Images missing alt text Alt text serves accessibility (screen readers) and gives Google context for image search. While Moz found alt text is a ranking factor for Google Images specifically, accessibility compliance is reason enough
RULE-41 Broken image sources (local file missing) A broken image leaves a blank space on the page. Users see it immediately. Google's page quality ratings penalize pages with broken media
RULE-42 Images without explicit width and height Wire auto-injects dimensions for ![alt](src) markdown images at build time. This rule fires when raw <img> tags bypass that injection or when the image file is unreadable (0-byte/corrupt). Use standard markdown image syntax
RULE-77 Images larger than 200KB Large images hurt page speed and Core Web Vitals. Compress or resize before deploy

Multilingual (3 rules)

These rules only activate when your pages use hreflang tags for multilingual content. If your site is single-language, they are skipped automatically.

Rule What it catches Why it matters
RULE-43 Invalid language codes in hreflang tags Hreflang uses BCP 47 language tags (en, de, fr, etc.). A typo like "eng" instead of "en" means Google ignores the tag and may show the wrong language version to searchers
RULE-44 Hreflang tags present but no x-default The x-default tag tells Google which page to show when no language matches the searcher's region. Without it, Google picks one and may pick wrong
RULE-45 Hreflang links without reciprocal confirmation If page A says "my German version is page B" but page B does not link back to page A, Google treats the hreflang as unconfirmed and may ignore it

Structured Data (5 rules)

JSON-LD structured data helps Google understand your content type, author, dates, and relationships. Invalid structured data is worse than none at all.

Rule What it catches Why it matters
RULE-45 Page has no JSON-LD structured data Every page should have at least one JSON-LD block declaring its type. Wire generates these automatically
RULE-46 Invalid JSON in JSON-LD script blocks Malformed JSON means Google cannot parse any of your structured data. A single missing comma or bracket breaks the entire block
RULE-47 Unrecognized schema.org type Using an @type that schema.org does not define means Google ignores the entire structured data block. Wire checks against 18 recognized types including Article, WebPage, Organization, Product, FAQPage, and BreadcrumbList
RULE-48 Broken URLs in structured data fields If your JSON-LD references a URL (for images, logos, or page links) that does not exist on your site, Google may flag the structured data as misleading
RULE-55 JSON-LD missing recommended fields for its type Google recommends specific fields per schema type (e.g. Article needs author, datePublished). Missing fields reduce rich result eligibility

Discovery (1 rule)

Rule What it catches Why it matters
RULE-53 Discovery step IDs that do not match any H2 anchor in the article Step IDs reference article sections. When a rewrite renames or removes an H2, the step's "Read the full article on this" button scrolls to nothing. Users see a broken experience. Wire catches this before deploy

Components (5 rules)

Rule What it catches Why it matters
RULE-63 Stats bar (:::stats) without exactly 4 items Desktop renders 4 across, mobile renders 2+2. Any other count (especially 3) creates an orphan row on mobile. Add or remove stats to hit exactly 4
RULE-64 Custom <section> in landing hero without section-hero class Wire wraps the first landing section with section-hero (light background, padding, max-width). If you provide your own <section>, add section-hero to its class list so Wire passes it through without double-wrapping. Otherwise your custom styling fights Wire's hero padding
RULE-68 Progress bar fill exceeds 100% Progress bars visually break when the fill width is greater than the container
RULE-105 Card links to external URL Cards are navigation elements. External links in cards confuse the internal linking structure
RULE-106 Mixed linked and unlinked cards in one block All cards in a :::cards block should be consistently linked or all unlinked
RULE-108 Markdown link in :::stats, :::banner, or :::progress These shortcodes render values as plain text, no markdown processing. [text](url) renders literally. Use :::badges for linked items

Content Quality (22 rules)

Content quality rules detect AI writing patterns, thin structure, and metadata problems that hurt rankings.

Rule What it catches Why it matters
RULE-52 Em dashes in body text Em dashes are a strong AI writing signal. Human writers use commas, periods, or semicolons. Rewrite the sentence
RULE-67 Headings with numbered prefixes (## 1. Introduction) Numbered headings are a mechanical writing pattern. Use descriptive headings
RULE-73 Duplicate meta descriptions across pages Each page needs a unique description. Duplicates dilute click-through signals
RULE-74 First image uses loading=lazy Lazy-loading the first image delays Largest Contentful Paint. Remove lazy from above-the-fold images
RULE-75 Pages more than 3 clicks from homepage Deep pages get less crawl attention. Keep important content within 3 clicks of the homepage
RULE-76 Article pages without date metadata E-E-A-T freshness signals require publication dates. Add created: to frontmatter
RULE-80 Identical opening paragraphs on different pages Template content that was not customized. Each page needs a unique introduction
RULE-83 More than 10% of body text is bold Excessive bold text is an AI writing pattern. Bold should highlight key terms, not entire paragraphs
RULE-84 List items exceeding 50 words Long list items are paragraphs hiding as bullets. Use actual paragraphs for detailed explanations
RULE-85 Pages over 500 words with no external links Longer content without external citations lacks source credibility. Link to evidence
RULE-86 Meta description starts with the title text Lazy metadata. The description should expand on the title, not repeat it
RULE-87 Many headings with very few words between them Outline-style content with no depth. Expand thin sections or consolidate headings
RULE-88 Single word exceeds 5% of body text Keyword stuffing. Reduce repetition and use natural language variation
RULE-89 Pages over 300 words with no H2 subheadings Long unstructured text. Add H2s to break content into scannable sections
RULE-91 Excessive H5/H6 heading depth Over-structured content. Most pages only need H2 and H3
RULE-93 Tables without thead Tables need <thead> for accessibility and search engine parsing
RULE-94 Meta description exceeds 160 characters Google truncates long descriptions. Keep under 160 characters
RULE-95 Title exceeds 60 characters Google truncates long titles in search results. Keep under 60 characters
RULE-96 No semantic HTML elements Pages should use <article> or <main> for content structure
RULE-97 Article body has no paragraph text Non-prose content in an article layout. Check if the layout is correct
RULE-99 Meta description under 70 characters Short descriptions underutilize the SERP snippet. Aim for 120-155 characters
RULE-100 Title and H1 share less than 30% keyword overlap Title about one topic, H1 about another. Align them
Rule What it catches Why it matters
RULE-32 Missing imprint/privacy links EU/German law requires imprint and privacy policy links. Wire checks for both

Indexability (2 rules)

Rule What it catches Why it matters
RULE-49 noindex on pages that other pages link to If pages link to a noindexed page, those links pass ranking power to a page Google will not show in search results. The noindex is usually accidental, a leftover from staging or a template error
RULE-50 nofollow on internal links rel="nofollow" tells Google not to follow a link or pass ranking power through it. On internal links, this wastes your own site's link equity. Internal nofollow is almost always a mistake

Where This Fits in Wire's Quality System

Wire enforces content quality at four levels. Each catches what the previous layers missed.

Layer When it runs What it checks Cost
Prevent Before the AI writes Styleguide rules taught to the AI before every prompt Free
Fix On every save 9 auto-corrections applied before content hits disk Free
Detect On demand (audit) 13 content-level checks across all pages Free
Verify After build (lint) 91 HTML-level checks across rendered site Free

The first three layers work on markdown, the raw content. Build verification works on the finished HTML, what search engines and visitors actually see. A page can have perfect markdown and still fail verification if a template forgets to insert a canonical tag, or if a Jinja2 partial omits the viewport meta tag.

This is why Wire runs verification after rendering, not before. The template layer can introduce problems that do not exist in the content layer. Wire catches them before they reach production.

What Wire Does Not Check at Build Time

Four checks from the original specification require a live HTTP server and are deliberately excluded from build verification:

  • Redirect chains (RULE-14): requires following HTTP redirects, which only work on a running server
  • 4xx/5xx status codes (RULE-27): requires HTTP requests to external URLs
  • Page load speed (RULE-38): requires a browser rendering engine (Lighthouse)
  • Mobile rendering (RULE-39): requires device emulation

These are server-level and network-level checks. Wire is a build tool. It works with files on disk. For live-server checks, use your search console's URL Inspection tool or a monitoring service like Pingdom or UptimeRobot.

Typical Results

On a real 316-page production site, the first build verification run found:

  • 4 pages with duplicate H1 tags across different sections
  • 12 internal links pointing to pages that had been renamed
  • 2 pages where the template failed to insert Open Graph tags
  • 1 page with a JSON-LD block containing a trailing comma (invalid JSON)

Total cost: zero. Every issue included the exact URL and a specific fix instruction. The same issues would have taken a manual review hours to find, if they were found at all.

Running Selectively

Not every check matters for every site. A single-language site does not need hreflang rules. A site without structured data does not need JSON-LD rules. Run only what applies:

# Only check page structure and internal links
python -m wire.lint --site site/ --rules RULE-01,RULE-02,RULE-03,RULE-05,RULE-07,RULE-33,RULE-35

# Only check sitemap consistency
python -m wire.lint --site site/ --rules RULE-23,RULE-24,RULE-25,RULE-26

Every rule ID is stable. You can save your preferred rule set and run the same checks on every build.