Skip to content

Make every sitemap lastmod track its page's content - #93

Merged
fosterwei merged 1 commit into
mainfrom
fix/sitemap-lastmod
Oct 13, 2026
Merged

fosterwei merged 1 commit into
mainfrom
fix/sitemap-lastmod

Conversation

@fosterwei

Copy link
Copy Markdown
Owner

68 of 69 dated sitemap URLs said 2026-09-29. Nine pages had no lastmod at all.

What was wrong

A date only moved when someone remembered a changelog entry, and in two weeks 35 of 55 records drifted. On 12 October nineteen compounds gained a calculator section and nineteen calculator pages were created; all thirty-eight still claimed 29 September. The calculator pages claimed a date before they existed.

Two sources, never mixed for one page

Pages Dated by Why
Compounds, stacks, comparisons, tools their own changelog they print the date three times (Updated pill, reference card, JSON-LD dateModified); the sitemap must agree with the page
Hubs, trust pages, calculators research/lastmod.json they print no date of their own

Backfill from history, never the build date

Before After
URLs with a lastmod 69 of 78 78 of 78
Distinct dates 2 5
Calculator pages 29 Sep (before they existed) 12 Oct
Compounds with the calculator section 29 Sep 12 Oct, with a changelog entry readers see
/privacy none 8 Oct, the GA4 change

Hub and trust pages take the git date of their source, checked against the record data they display; nothing they show changed later.

One judgment, logged rather than made silently. The 30 September change relabelled a compare-table column and trimmed a sentence from changelog text on 34 records. That is cosmetic, and Google asks for lastmod to move on significant updates only, so those records keep their earlier dates. The reason is recorded in the manifest.

A correction to my own entries

Four of the nineteen new entries first said the calculator page "charts syringe units". For humanin, melanotan II, selank and TB-500 it does not: every published dose is per kilogram, and those pages explain why there is no chart. The entries now say that.

The guard

scripts/check_lastmod.py fingerprints the text, tag names and link targets inside <main> of every sitemap page and compares against the manifest. Scripts, styles, classes and ids are excluded, so build noise does not count.

Scenario Result
Record content changed, no changelog entry fails, says what to do
Same change, --write --trivial "reason" passes, date kept, reason logged
Hub content changed fails; --write stamps today
lastmod in the future fails
Only Astro class hashes changed passes

It also caught a real case unprompted: correcting those four entries changed four pages, and it flagged all four.

Runs in CI after the build. Workflow documented in docs/content-automation.md.

Gates

Content validation, registry, 36 drafter tests, 55 calculator tests, loop gate 47/47, all eight link rules, lastmod guard.

🤖 Generated with Claude Code

68 of 69 dated URLs said 2026-09-29. Nine pages had no date at all.

lastmod only moved when someone remembered a changelog entry, and in two
weeks 35 of 55 records drifted. On 12 October nineteen compounds gained a
calculator section and nineteen calculator pages were created; all
thirty-eight still claimed 29 September, so the calculator pages claimed
a date before they existed. Google treats a sitemap whose dates are wrong
as a sitemap whose dates can be ignored.

The fix keeps two sources and never mixes them for one page. Record pages
stay dated by their own changelog, because they print that date three
times (the Updated pill, the reference card, JSON-LD dateModified) and the
sitemap has to agree with the page. Everything that prints no date, the
hubs, the trust pages and the calculators, is dated from
research/lastmod.json.

Backfill used history, never the build date. The nineteen compounds get a
dated changelog entry for the calculator section, which readers see. Hub
and trust pages take the git date of their source, checked against the
record data they display: nothing they show changed later. Calculator
pages take 2026-10-12, the day they were created and rewritten. Records
whose latest change was the 2026-09-30 relabelling of a table column keep
their earlier dates, because that change is cosmetic and Google asks for
lastmod to move on significant updates only. That judgment is logged in
the manifest rather than made silently.

Four of the nineteen entries first claimed the calculator page charts
syringe units. For humanin, melanotan II, selank and TB-500 it does not:
every published dose for them is per kilogram, and those pages say why
there is no chart. The entries now say that instead.

scripts/check_lastmod.py fingerprints the text, tag names and link
targets inside <main> of every sitemap page and compares them with the
manifest. A record whose content changed without a newer changelog date
fails CI with a message saying what to do; --trivial "reason" accepts a
cosmetic change and logs the reason; --write stamps today on pages that
print no date. Scripts, styles, classes and ids are excluded, so build
noise such as Astro's scoped-style hashes does not count as a change.

Verified five ways: an unrecorded record change fails, the same change
accepted as cosmetic passes with its date kept and the reason logged, a
hub change fails then is stamped by --write, a future date fails, and
changed Astro class hashes pass. It also caught a real case while this
was being written: correcting those four entries changed four pages, and
the guard flagged all four.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@vercel

vercel Bot commented Oct 13, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
research-reference Ready Ready Preview Oct 13, 2026 7:16am UTC

@fosterwei
fosterwei merged commit a533bfd into main Oct 13, 2026
5 checks passed

This branch was successfully deployed

1 active deployment
Preview — e4c25b02 Deployed Oct 13, 2026 by vercel[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant