Skip to content

seo: roll up sitemap lastmod from newest descendant on branch pages - #21036

Open
workprentice[bot] wants to merge 1 commit into
masterfrom
seo/daily-20260821
Open

seo: roll up sitemap lastmod from newest descendant on branch pages#21036
workprentice[bot] wants to merge 1 commit into
masterfrom
seo/daily-20260821

Conversation

@workprentice

@workprentice workprentice Bot commented Aug 21, 2026

Copy link
Copy Markdown
Contributor

What

Sitemap <lastmod> for section/taxonomy/term/home pages now rolls up to the newest lastmod among the pages they render, instead of reporting only the branch page's own _index.md git-commit date.

Why

Root-caused today via a live sitemap audit: /blog/ reports lastmod 2024-05-16T10:42:54-07:00 (826 days old) while its newest post, "Compatibility Testing Pulumi HCL," published 2026-08-14. Sixteen URLs share that identical stamp (/blog/, /why-pulumi/, /automation/, /careers/, and others), all touched by the same one-time bulk commit -- enableGitInfo: true sets .Lastmod from the content file's git history, and Hugo does not roll a branch page's .Lastmod up from its descendants.

That is not merely stale, it is actively wrong: /blog/ changes constantly and the sitemap is claiming otherwise. Gary Illyes confirmed (2026-07-16) that Google evaluates lastmod trustworthiness sitewide, not per-URL, and stops trusting the field across an entire site once it catches enough incorrect dates -- so a wrong date on a high-traffic index page carries a cost well beyond that one URL.

Fix

In layouts/_default/sitemap.xml, for pages whose .Kind is home, section, taxonomy, or term, compute lastmod = max(own .Lastmod, newest descendant's .Lastmod via .RegularPagesRecursive) before emitting it. Leaf pages (.Kind == "page") are untouched, so genuinely static, old-but-honest content -- /tutorials/glossary/*, /trademark/, /acceptable-use/, /awards/ -- keeps its real date. No fabricated dates: the rollup only ever surfaces a real, already-existing .Lastmod value from a real descendant page.

Verification

Built an isolated Hugo harness (minimal site, real layouts/_default/sitemap.xml template, synthetic content tree with explicit lastmod front matter) covering:

  • A section with a newer and an older child -> section reports the newer child's date, each leaf keeps its own.
  • Two levels of nesting -> the outer section's date rolls up transitively through the inner subsection.
  • A taxonomy list page and one of its terms -> both roll up across tagged content.
  • A section with zero children -> keeps its own date (no crash, no false rollup).
  • Standalone leaf pages -> completely unaffected.
  • Home page -> rolls up across the whole site, as expected.

All eleven pages in the harness sitemap emitted exactly the expected <lastmod> value; no XML malformed, no unexpected date on any leaf.

Scope

layouts is fully excluded from prettier (.prettierignore), so no formatting step applies. This is a template-only change with no content or front-matter edits.


🧠 This PR was created by workprentice on behalf of the Pulumi SEO/AEO daily optimization assignment.

Section, taxonomy, term, and home pages in the sitemap were reporting only their own _index.md git-commit date, not the newest date among the content they render. This produced demonstrably wrong lastmod values -- e.g. /blog/ claimed 2024-05-16 while its newest post published 2026-08-14, a 2+ year gap on a page that changes constantly.

Google treats sitemap lastmod reliability as a sitewide signal (Gary Illyes, 2026-07-16): once it catches enough wrong dates it stops trusting lastmod across the whole site, not just the offending URLs. A stale date on a high-traffic index page like /blog/ is worse than omitting lastmod entirely.

Fix: for Kind in (home, section, taxonomy, term), compute lastmod as max(own .Lastmod, newest .RegularPagesRecursive .Lastmod) before emitting it. Leaf pages are untouched -- old-but-honest leaves like /tutorials/glossary/* and static pages like /trademark/ keep their real dates.

Verified in an isolated Hugo harness: nested sections roll up transitively, taxonomy/term pages roll up across tagged content, a section with no children keeps its own date, and leaf pages are unaffected.
@github-actions github-actions Bot added review:triaging Claude Triage is currently classifying the PR domain:infra PR touches workflows, scripts, infra, Makefile, or build config review:in-progress Claude review is currently running and removed review:triaging Claude Triage is currently classifying the PR labels Aug 21, 2026
@github-actions

Copy link
Copy Markdown
Contributor

Pre-merge Review — Last updated 2026-08-21T12:21:45Z

Tip

Summary: Infrastructure-only change to layouts/_default/sitemap.xml: for branch pages (home, section, taxonomy, term) the emitted <lastmod> is now the newest of the page's own .Lastmod and every descendant regular page's .Lastmod, instead of the branch page's own commit date. That's the right SEO behavior — a section index whose children changed yesterday should no longer advertise a two-year-old lastmod. The template logic reads correctly: $lastmod is seeded from .Lastmod so leaf pages are unaffected, outer-scope assignment inside range is valid in modern Hugo, and the zero-time guard now tests the rolled-up value. The kind of wrongness that would matter here is silent output corruption (a malformed or missing lastmod for every URL) or a build-time regression on a site this large; I reviewed the template semantics for the former and flagged the latter as a thing to watch. No content files changed, so no prose, link, frontmatter, or code-example passes applied.

Review confidence:

Dimension Level Notes
mechanics MEDIUM Template logic reviewed by reading; CI doesn't run hugo here, so the rendered sitemap and full-build timing weren't observed.
facts HIGH No factual claims — the diff is template code.
Investigation log
  • Cross-sibling reads: not run (not in a templated section)
  • External claim verification: 0 of 1 claims verified (0 unverifiable, 0 contradicted) · 4 specialists (numerical, cross-reference, capability, framing); 0 cross-specialist corroborations · routed: 0 inline, 0 Pass 1, 0 Pass 2, 1 Pass 3 (verified 0, contradicted 0, unverifiable 1).
  • Cited-claim spot-checks: not run (no cited claims)
  • Frontmatter sweep: not run (no frontmatter in diff)
  • Temporal-trigger sweep: not run (no trigger words)
  • Code execution: not run (no static/programs/ change)
  • Code-examples checks: not run (no fenced code blocks in content files)
  • Editorial-balance pass: not run (not under content/blog/)
🚨 Outstanding ⚠️ Low-confidence 💡 Pre-existing ✅ Resolved
0 1 0 0

🔍 Verification trail

2 claims extracted · 0 verified · 1 unverifiable · 0 contradicted
  • L13 in layouts/_default/sitemap.xml "{{ safeHTML ( $lastmod.Format '2006-01-02T15:04:05-07:00' ) }}{{ end }}{{ with .Sitemap.ChangeFreq }}" → ➖ not-a-claim (evidence: This is a Hugo template snippet formatting a lastmod date field using Go time layout syntax; it is code/template logic, not a factual or falsifiable assertion.; source: layouts/_default/sitemap.xml)
  • L7 in layouts/_default/sitemap.xml "nested RegularPagesRecursive walk runs once per branch page, including the home page" → 🤷 unverifiable (evidence: build-time cost depends on total page count on a full registry build, which this review environment does not run; source: reading of the template's loop nesting)

🚨 Outstanding in this PR

No outstanding findings in this PR.

⚠️ Low-confidence

Review each and resolve as appropriate — these don't block the PR.

  • [L7] layouts/_default/sitemap.xml — the roll-up nests a RegularPagesRecursive walk inside the .Data.Pages loop, so on a full build every branch page re-walks its entire subtree, and the home page walks the whole site. On a small site that's free; on a full registry build it's roughly the sum of every branch page's descendant count. Flagged 🤷 unverifiable because CI doesn't run hugo for this job, so I couldn't measure it. Worth a glance at the sitemap-generation time in the next full build before merge; if it moves, the usual fix is to compute the roll-up once per section rather than per page in the output loop.
    • Two smaller things to consider while you're in here, neither blocking: the walk includes descendants that the if not (or .Params.private ...) guard excludes from the sitemap, so a private or block_external_search_index page can still bump its parent's lastmod; and because only regular pages are walked, a nested _index.md edited on its own won't propagate up to its ancestors. Both are defensible as-is — the first arguably reflects real content churn — but they're the two places the emitted date won't match what a reader would guess.

💡 Pre-existing issues in touched files (optional)

No pre-existing issues in touched files.

✅ Resolved since last review

No items resolved since the last review.

📜 Review history

  • 2026-08-21T12:21:45Z — Sitemap lastmod roll-up reads correct; no blockers, one note on the nested per-branch-page walk's build cost (9b4253d)

  • Refresh this review — comment @claude #update-review. Say what you fixed, or which finding you dispute and why; both work in the same mention.
  • Ask for anything else — comment @claude with no hashtag (questions, one-off fixes). Leaves this review untouched.

Important

Please don't hide, resolve, or delete this comment! It breaks things!

📖 How pre-merge review works — the full lifecycle, short-circuits, and escape hatches.

@github-actions github-actions Bot added review:no-blockers Claude review completed cleanly; outstanding is empty and removed review:in-progress Claude review is currently running labels Aug 21, 2026
@pulumi-bot

Copy link
Copy Markdown
Collaborator

@pulumi-bot

pulumi-bot commented Aug 21, 2026

Copy link
Copy Markdown
Collaborator

Lighthouse Performance Report

Commit: 9b4253d | Metric definitions

Page Device Score FCP LCP TBT CLS SI
Homepage Mobile 🔴 32 3.0s 8.2s 3537ms 0.072 8.4s
Homepage Desktop 🟢 92 1.2s 1.5s 0ms 0.012 1.2s
Install Pulumi Mobile 🟡 57 7.6s 16.1s 25ms 0.000 7.6s
Install Pulumi Desktop 🟢 92 1.2s 1.5s 0ms 0.022 1.2s
AWS Get Started Mobile 🟡 60 5.4s 7.2s 2ms 0.102 5.4s
AWS Get Started Desktop 🟢 92 1.1s 1.5s 0ms 0.071 1.1s

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

domain:infra PR touches workflows, scripts, infra, Makefile, or build config review:no-blockers Claude review completed cleanly; outstanding is empty

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants