On 29 October I added a single link to this site’s footer. One <a>, repeated across 82 pages by a find-and-replace. The next time the sitemap was generated, 61 of its 121 URLs carried <lastmod>2026-10-29</lastmod> — most of the site announcing a change that was one line of markup.
Nothing had broken. The generator was doing what nearly every sitemap script does: take the date of the last commit that touched the file. That rule is correct about Git and wrong about content.
Why a date nobody reads is worth caring about
Google documents that it ignores <changefreq> and <priority> outright, and that it uses <lastmod> only when the value is consistently accurate. That leaves lastmod as the one element in the file with something to lose. A site that marks everything fresh whenever it changes a font is not getting crawled harder; it is teaching a crawler to stop reading the field. And the failure is silent — nothing in Search Console warns you that your sitemap cries wolf.
A sweep is not an edit
The useful distinction turned out to be simple. A commit that touches twenty or more HTML files is almost always a site-wide pass: a font swap, an analytics snippet, a nav link, a radius scale. The largest one in this repo touched 138 pages and changed a border radius. Under the naive rule, that commit tells Google 138 pages changed.
But you cannot simply ignore sweeps, because they are not always empty. A copy pass on 11 October touched most of the site and genuinely rewrote a handful of pages inside it. So the question is not was this a sweep but did this page get more than the sweep gave everyone else:
SWEEP_FILES = 20 # a commit touching this many html pages is a site-wide edit
SWEEP_MULT = 3 # ...and counts for a page only above 3x that commit's median lines
SWEEP_FLOOR = 10 # and above this, so a median of 1 cannot let a 2-line edit through
Median, not mean: one genuinely rewritten page drags a mean up far enough to let the whole sweep through behind it. The floor matters for the same reason in the other direction — a footer-link sweep has a median of about one line, and 3× that would admit any three-line change.
I measured the constant instead of picking one
The first version of this rule was the obvious one, and it was wrong in a way that looked right. So before settling on 3×, I ran the candidate over all 34 site-wide commits in the repo’s history and looked at what each one would keep. At 3× the median with a floor of 10, each sweep credits between zero and six pages. The font swap, the radius scale and the footer link count for nobody. A page rewritten inside the 11 October copy pass — 117 lines against a median of 4 — keeps that date, which is the outcome you want.
Line count is a proxy for “did the content change”, and it is deliberately a biased one: it errs toward the older date. Understating freshness costs almost nothing. Overstating it is the thing that gets the signal discarded.
The other half of the bug: the list was typed by hand
The same file had a second defect that the first one hid. The set of pages was a constant in the script, maintained by hand, and six indexable pages were missing from it — among them the press page, which exists so that a writer can find us, and three per-game privacy pages. Nobody left them out on purpose. A hand-kept list of 121 entries simply drifts away from the directory it describes.
Pages are discovered from disk now, and a page is included unless it says otherwise: a robots noindex or a meta-refresh stub leaves it out. That one rule keeps our 22 templated $1 tools out of the sitemap — they were noindexed deliberately, being exactly the thin near-duplicate pages Google says it devalues — and keeps an unreleased game page out while it is unreleased, with nobody having to remember either.
What actually changed
| In sitemap.xml | Before | After |
|---|---|---|
| URLs listed | 121 | 127 |
| Entries claiming 2026-10-29 | 61 | 4 |
changefreq and priority elements | 121 each | 0 |
The four remaining 10-29 dates are the pages that really were written that day.
Worth checking rather than assuming: the file is valid against xmllint, contains no page that asks to be noindexed, and a second run reproduces the committed file byte for byte — which is what makes it safe to regenerate on every post.
What this does not fix
Lines are not meaning. A whitespace reflow of one page inside a sweep still clears the bar, and a one-word fix to a headline — which genuinely changes what a page says — does not. The threshold is tuned to this repo: 3× the median is not a universal constant, it is the number 34 sweeps here supported, and a repo whose sweeps are formatting-heavy would want a different one. And none of it matters for a page not worth recrawling in the first place.
If you generate a sitemap, the check costs a minute. Count how many of your lastmod values share a single date, then go and look at what that commit did. If the answer is “added a link to the footer”, your sitemap is telling a crawler something you would not say out loud.
