# Verifying published documentation

Verification happens twice. Before publication the gate runs over the registry — shape, claims, links, search intent — and refuses anything malformed. After the deploy the checks move to the live tree: built HTML and its Markdown twin, structured data that agrees with the visible page, sitemaps that match the corpus, and a fetch as each target crawler. Only then is anything submitted.

- **Status:** Available
- **Audience:** developer, owner
- **Last verified:** 2026-09-10
- **Canonical:** https://connectbyjbrh.com/docs/workflows/docs-verification/

## Two passes, asking two different questions

The pre-publication pass asks whether the corpus is well-formed and honest: is this page complete, does it claim only what the capability registry supports, does every link resolve, does any other page already own its primary query. It runs against the source records, so it is fast and it can be run after every edit.

The post-deploy pass asks a question the source cannot answer: does the thing on the internet behave. A page can be perfect in the registry and unreachable in production, because something between the requester and the application refused it — an edge rule, a bot filter, a challenge page, a geographic block. None of that appears in the application's own logs as an error, and none of it appears in a browser, because a browser is not what was blocked.

## The verification chain

1. Trigger — a documentation change is ready, or a deploy has just finished.
2. User event — somebody runs the checks. They are ordinary commands, not a hidden step in a pipeline, so the same run is reproducible by anyone.
3. Authentication and workspace resolution — none. Every artefact checked here is public by definition.
4. Ingest — the plan and the content modules are joined into page records; the built tree is read from disk where the check needs the output rather than the source.
5. Canonical record — the joined page record is what every pre-publication check judges.
6. Classification — checks are grouped by what they can prove: shape, claims, links, structured data, output, reachability.
7. Knowledge and rules — the capability registry, the protocol record and the schema's own limits are the standards being checked against.
8. Approval — the gate fails closed. Its exit status is the number of distinct failure classes, so a report is a list of kinds of problem rather than a wall of instances.
9. Action — the live fetch: the built pages, their Markdown alternates, the manifests, the sitemaps, and one request per target user agent.
10. Result — a page-by-page verdict. A 200 carrying real content is the only pass; a 200 carrying an empty shell is a failure that looks like a success.
11. Timeline — what changed is recorded in the changelog, which is itself a published artefact and is checked like any other.
12. Audit and submission — the changed URLs are submitted only after the live pages have passed. Submitting first is how a feed stops being trusted.

## What each check proves, and what it cannot

| Check | What it proves | What it cannot see |
|---|---|---|
| The gate over the registry | Shape, word floors, banned phrasing, forbidden claims, duplicate ids, URLs and search intent | Anything about the generated output or the live site |
| The link graph | Every internal target exists, and no page is unreachable from the rest | A link the renderer produced rather than an author wrote — unless it is run over the built HTML |
| Structured data and manifests | JSON-LD parses, manifests and sitemaps are well-formed, and the machine descriptions carry the fields their specifications require | Whether the claims are true — which is why it also checks agreement with the page |
| Agreement with the page | That a headline matches the H1, a modified date matches the page, and an FAQ block contains the questions actually rendered | A page that is wrong in both the text and the structured data at once |
| The built tree | One H1, one title, a canonical link and a Markdown twin per page; every public page in exactly one sitemap | Whether a requester can actually fetch it |
| The crawler probe | What each named user agent receives from the live site | Nothing else — this is the only check that sees an edge rule at all |

> **Note** Structured data that disagrees with the visible page is worse than none: it is a claim to a search engine that a reader cannot check. That is why agreement, not just validity, is asserted.

## Reading a failure without over-reacting

1. Read the failure class before the instances. Thirty broken links from one renamed page is one problem, not thirty.
2. Fix the source, not the output. The generated tree is derived; editing it produces a change that the next build silently reverses.
3. A pre-existing failure that your change did not cause is established once and left alone unless it is yours to fix.
4. Do not submit anything to an index until the live pass is clean. A URL advertised while it 404s costs more than the delay.

The same checks run inside the authoritative test suite as well as by hand, so a code change that moves the ground under a document fails the run instead of waiting to mislead a reader. One implementation, two front doors.

## Questions

### Why check the built HTML when the source already passed?

Because the renderer is code, and code has its own failures. A breadcrumb pointing at a section that does not exist, a footer link to a renamed page or a missing Markdown alternate are all produced during generation and are invisible in the source records.

### What counts as a page being live?

A 200 containing the real content, fetched as the agent that matters. A 200 with an empty shell, a redirect to a sign-in, or a challenge interstitial all mean the page is not reachable, whatever the status line says.

### Is a clean gate enough to publish?

It is enough to commit. Publishing also means the deploy happened, the built pass is clean against the live site, and only then are changed URLs advertised. The order is the point.

## Related

- [Publishing a documentation change](https://connectbyjbrh.com/docs/workflows/docs-publication/)
- [Submitting changed URLs to IndexNow](https://connectbyjbrh.com/docs/workflows/indexnow-submission/)
- [A search crawler fetching a page](https://connectbyjbrh.com/docs/workflows/crawler-access/)
- [Measuring whether AI systems can actually read your site](https://connectbyjbrh.com/research/measuring-ai-visibility/)
- [The machine-readable documentation](https://connectbyjbrh.com/developers/machine-manifests/)
- [A probe that fires on a correct release is worse than no probe](https://connectbyjbrh.com/research/probes-that-cry-wolf/)

## What this page is based on

- `tools/docs_check.py` — the gate and the built-tree pass
- `tools/docs_link_check.py` — the registry link graph and the built-HTML pass
- `tools/docs_schema_check.py` — validity, and agreement between structured data and the page
- `tools/docs_crawler_probe.py` — what each user agent actually receives
- `tools/test_docs_match_code_v1.py` — the same checks inside the authoritative suite
