GenerateSaaS

Editorial checklist

What the schema refuses, what the template guarantees, and what is left for you to check before an alternatives roundup, a compare pair page or an article goes live.

Three layers stand between a draft and a page worth indexing. The schema and the guard refuse a thin page, the template renders the parts that are structural, and the rest is the author's judgement.

Refused by the schema

A page missing any of these is noindexed and rendered degraded until you fix it. It never fails the build.

TemplateRequired
alternativesdescription, lastVerified, author, competitor.name and competitor.url, a verdict summary, and at least three items, each needing a product slug, a startingPrice (or factStrip), a bestFor (or features / pros) and a fallsShort (or cons). One roundup per anchor: index.md is yours, any other file needs competitor.external, plus competitor.slug for its table to lead with the anchor
comparePer product: name, url, tagline, bestFor, priceFrom, order, pros, cons, one facts band. A PAIR page additionally needs the quality gate - 10+ shared rows, an authored 3-line-per-side verdict, priceFrom both sides, lastVerified within 180 days on both. The optional index.md needs title, description, primaryKeyword, intro, 3+ criteria and 4-6 faq, or the hub drops back to a plain router
anyNo reserved first slug segment, no index.md in an article section (its listing shadows the file), and no primaryKeyword another document in the same locale already claims

In development a banner on the page lists its guard errors by name, and the build prints one summary line naming the offending paths. Two authoring errors are the exception and do fail the build, because each loses verified content silently: an alternatives page authoring its own matrix:, and a product or competitor.slug naming no fact sheet.

Guaranteed by the template

You never have to remember these; they render on every page.

  • A That is us badge wherever your own product is one of the entries, and a byline carrying the year - no month reaches a reader, though the JSON-LD, sitemap and dateTime attributes keep their full dates.
  • Every published comparison page is linked from the hub, and each product's own pages hang in that product's block - so a link appears exactly when its page does and never outlives it.
  • Proof sits beside the claim it supports. A block's heading never links out; the vendor's site is a named Visit link at its foot.
  • FAQ answers are native <details>, closed on the comparison pages and still in the HTML, so a crawler with no JavaScript reads them.
  • A CTA row pointing at config.routes.pricing and contact in the hero, and a CTA card below the body.

Yours to check

Answer first. The page opens with the verdict, and every section opens with its own answer. Every H2 reads correctly out of context.
One primary keyword per page, matching how someone actually types the question.
FAQ: 4 to 8 questions, 2 to 4 sentence answers, the first one the blunt query ("Is X better than Y?").
Turn the TOC on past roughly 1,500 words with features: { toc: true }. It is off by default on alternatives and compare pages.
Screenshots carry alt text and a caption. A screenshot with neither is decoration.
Prices are dated and both-sided. Quote-only tiers as price: "Custom" with the drivers in includes; legacy plans in pricing.note.
Publish the cluster's original statistic once, as a page document, and link every spoke to it rather than repeating it.
No slop. No superlative crowns, no badmouthing the competitor, no claim you cannot source.
Cap the cluster. A dozen or two pages, then expand on what the data says is working.
Refresh what the build flags. It names the pages last verified or modified over 180 days ago; re-check their facts and bump lastVerified.

Check your CDN or WAF does not block AI crawlers. robots.txt allows them and config.agents.crawlers names them, but neither can override an edge rule that returns 403 before the request reaches your app. Test with a request carrying the crawler's user agent against your production domain, not localhost.

On this page