Build AIOSEO

YESback to the verdict

0%0 of 17 items done

Saved on this device only. Tick prerequisites first, then work the phases in order · do not start one until the checks above it pass.

A technical SEO layer whose value is correctness rather than volume: sitemaps that match what is actually indexable, canonical and redirect handling that does not contradict itself, and structured data emitted with an honest record of whether it still does anything.

estimated effort multi-daythe files for this build are in the project pack

CrawlingA local crawler over your own siteRulesIndexability derived from directives, canonicals and redirects togetherSitemapsGenerated from what is actually indexable, not from what existsStructured dataEmitted with a dated support table per typeRuntimeTypeScript with SQLite

Before step 1

Everything below is assumed from the first step. Tick each one when you actually have it, not when you plan to.

  1. installfree

    Why Runs the crawler and the report generator.

    Get it nodejs.org, or nvm install 22. open ↗

    Verify node --version prints v22 or higher

  2. have readyfree

    Why Contradicting signals, redirect chains and sitemap divergence only appear on a site with history, and a fresh site has none of them.

    Get it Your own site, or one you have permission to crawl, ideally one that has been through a migration.

    Verify Five hundred pages crawl and at least one redirect chain longer than one hop exists

  3. have readyfree to read

    Why The build keeps a dated table per type, and it has to be populated from the search engine's own current documentation rather than from memory.

    Get it Read the current rich results documentation and record, per type you intend to emit, whether it still produces a result and the date you checked. open ↗

    Verify A dated support record exists per emitted type with a source link

  4. free

    Why It is the only source for what the search engine actually did with your pages, as opposed to what your crawler thinks it should have.

    Get it Verify ownership of the site in the search engine's own console.

    Verify Index coverage data is visible for the site

  5. decidefree

    Why A report padded with low-value warnings is a report nobody reads, so the inclusion rule belongs before the checks are written.

    Get it Decide the rule: an issue is reported only if you can state its mechanism in one sentence. Write the list.

    Verify Every planned check has a one-sentence mechanism written down

Data model

Create these before the first phase that stores anything. Changing a table later is the expensive kind of change.

- pages(id, url, status, title, meta_description, canonical, robots_directives, indexable, reason)
- links(from_page_id, to_url, rel, is_internal)
- redirects(from_url, to_url, status, chain_length, resolved_target, at)
- sitemap_entries(url, lastmod, included, exclusion_reason) · exclusions recorded so the sitemap is explicable
- schema_types(name, produces_rich_result, deprecated_on, checked_on, source_url) · dated, because this changes
- schema_emissions(page_id, type_name, valid, warnings_json)
- audits(id, run_at, pages_crawled, issues_by_kind_json)
- A page's indexability is derived and stores its reason, because the interesting cases are where three signals disagree.

Environment variables

These go in a .env file the app reads at startup. The pack's .env.example is this table as a file · copy it, never commit the filled-in version.

VariableNeededExampleWhere the value comes from
DB_PATHrequired./seo.dbCrawl results and audit history.
SITE_URLrequiredhttps://example.comThe site to crawl. Yours, or one you have permission for.
CRAWL_DELAY_MSrequired500Even your own site should not be hammered by your own tool.
SCHEMA_SUPPORT_PATHrequired./config/schema-support.jsonDated support per type. The file that stops the tool reporting dead features as wins.
SUPPORT_RECHECK_DAYSrequired90After this a support record is surfaced as stale. Rich result types get retired.
REPORT_ONLY_WITH_MECHANISMrequiredalwaysPresent as a constant. An issue you cannot explain in a sentence is padding.

The build, in order

  1. Seeing the site as a crawler does

    You know which pages are actually indexable and why.

    1. Status, directives, canonical, crawl rules. Then derive.

      Files src/crawl.ts

    2. The reason is the useful output, not the boolean.

      Files src/indexable.tssrc/indexable.test.ts

    3. Where signals disagree is where the real problems are.

      Files src/contradictions.ts

    done when · tick each as it passes
    watch out
    • Treating a page as indexable because it returns a successful status ignores a noindex directive, a canonical pointing elsewhere or a robots rule blocking the crawl, and those three disagree with each other on real sites constantly. Derive indexability from all the signals and report the contradiction rather than picking one.
  2. Redirects that do not chain

    Every redirect resolves in one hop and no loop exists.

    1. Each hop works fine. The chain is the problem and only shows end to end.

      Files src/redirects.ts

    2. A cap and a report, not an infinite follow.

      Files src/redirects.ts

    3. A chain ending at a noindexed page is entirely wasted.

      Files src/redirects.ts

    done when · tick each as it passes
    watch out
    • A redirect chain accumulates over years as pages move, and each hop loses a little of whatever the original link was worth, but nothing ever reports it because each individual redirect works fine. Resolve the whole chain and report its length, since the problem is only visible end to end.
  3. A sitemap that tells the truth

    The sitemap contains exactly what is indexable and nothing else.

    1. Not from the page list. A sitemap listing noindexed pages is a contradiction.

      Files src/sitemap.ts

    2. So a missing page is explicable rather than mysterious.

      Files src/sitemap.ts

    3. Regenerating it every build teaches crawlers to ignore it.

      Files src/lastmod.tstests/lastmod.test.ts

    done when · tick each as it passes
    watch out
    • A last-modified date regenerated on every build tells search engines that every page changed every day, which trains them to ignore the field entirely. Derive it from actual content change, and leave it out rather than inventing one.
  4. Structured data, with an honest support table

    You know which types still produce a result and which are decoration.

    1. With a source and a checked date. This changes and your tool should know.

      Files config/schema-support.json

    2. Emitting invalid markup is worse than emitting none.

      Files src/schema.ts

    3. Emitting is activity. Producing a result is an outcome.

      Files src/report.tstests/no-fake-wins.test.ts

    done when · tick each as it passes
    watch out
    • Reporting structured data emission as an improvement is measuring your own activity, and it stays wrong for years because nobody revisits it after a search engine retires a feature. Keep the support state as dated data with a source, and surface entries that have not been rechecked recently.
what this build does not replace
after v1, if you want it

Need the files? The project pack on the verdict page hands your agent the whole brief · more seo & marketing.