Fix dangling #website reference and bind WebPage isPartOf/inLanguage - #20640
Conversation
Every collector partial that emits isPartOf (e.g. blog-entity.html on all 809 blog posts) points at "https://www.pulumi.com/#website", but that @id was only ever defined inside the .IsHome branch of graph-builder.html. On every non-home page the else branch appended only $orgMinimal — no WebSite node — so isPartOf resolved to nothing in that page's own @graph. Google's structured-data parser and AI/LLM extractors evaluate each URL's JSON-LD independently and do not dereference @id links across pages, so this was a dangling reference on roughly 2,500+ non-home content pages, 809 of them blog posts. Also, the root WebPage container ($webpage, defined at the top of graph-builder.html) never bound itself to the site entity or declared its language: no isPartOf, no inLanguage, on any page including the homepage. Two edits, one file: - Add a lean $websiteMinimal WebSite node in the non-home branch, mirroring the $orgMinimal precedent immediately above it: same @id/url/name/alternateName/description/inLanguage/publisher as the homepage's rich WebSite node so the two cannot drift out of sync. Deliberately omits "about"/"mentions" (homepage entity-definition payload that would bloat every page) and any potentialAction/ SearchAction (grep confirms zero SearchAction usage in this repo today, and Google retired the sitelinks search box, so adding one would not be an improvement). - Merge isPartOf + inLanguage onto $webpage before it is appended to the graph, using the file's established merge-dict idiom already used for description/breadcrumb/mainEntity/hasPart. Resolves on the homepage (rich node) and every other page (new lean node) alike. Verified no other dangling #website reference exists in layouts/. The four isPartOf sites in utils/product-entities.html point at a different, self-describing embedded object (https://www.pulumi.com/product/#pulumi-platform with inline @type/name) rather than a bare cross-graph @id, so they are not affected by this class of bug. Brace-balance check: {{ / }} count in graph-builder.html goes from 103/103 (baseline) to 107/107 (this change), i.e. balanced.
Pre-merge Review — Last updated 2026-08-02T12:16:10ZTip Summary: This is a template-only change to the JSON-LD Review confidence:
Investigation log
🔍 Verification trail6 claims extracted · 0 verified · 0 unverifiable · 0 contradicted · 2 mismatched
🚨 Outstanding in this PRThese must be resolved or refuted before merging.
|
|
Your site preview for commit 50619ea is ready! 🎉 http://www-testing-pulumi-docs-origin-pr-20640-50619ea4.s3-website.us-west-2.amazonaws.com |
Lighthouse Performance ReportCommit: 50619ea | Metric definitions
|
|
Status check: all CI gates are green (build, test matrix, triage, Pre-merge Review, Update Changelog) as of commit 50619ea, no conflicts. This has been sitting at |
What
Fixes a dangling
@idreference and completes theWebPagecontainer's binding to the site entity in the@graphstructured data builder.The bug
Every collector partial that emits
isPartOf— most visiblyblog-entity.html, which runs on all 809 blog posts — points at"https://www.pulumi.com/#website". That@idwas only ever defined inside the.IsHomebranch ofgraph-builder.html. On every non-home page, theelsebranch appended only$orgMinimal— noWebSitenode at all — soisPartOfresolved to nothing in that page's own@graph.Google's structured-data parser and AI/LLM extractors evaluate each URL's JSON-LD independently; they do not dereference
@idlinks across pages. So this was a dangling reference on roughly 2,500+ non-home content pages, 809 of them blog posts.Separately, the root
WebPagecontainer ($webpage, defined at the top of the file) never bound itself to the site entity or declared its language: noisPartOf, noinLanguage, on any page — including the homepage.The fix
Two edits, one file (
layouts/partials/schema/graph-builder.html):$websiteMinimalWebSitenode in the non-home branch, mirroring the existing$orgMinimalprecedent immediately above it. Shares the same@id/url/name/alternateName/description/inLanguage/publisheras the homepage's richWebSitenode, so the two cannot drift out of sync.isPartOf+inLanguageonto$webpagebefore it's appended to the graph, using the file's establishedmerge $webpage (dict ...)idiom already used fordescription/breadcrumb/mainEntity/hasPart. This resolves on the homepage (rich node) and every other page (new lean node) alike.Deliberate omissions (documented so no one re-litigates these)
about/mentionson the new leanWebSitenode — that's homepage entity-definition payload (the "what is Pulumi"Thingand competitor/technologymentionslist) and would bloat every single page unnecessarily. It stays on the homepage's rich node only.potentialAction/SearchAction— grepped the repo; zeroSearchActionusage exists today. Google retired the sitelinks search box feature, so adding one here would not be an improvement, on this page or any other.Verification
grep -rn 'isPartOf|#website' layouts/confirms no other dangling#websitereference exists. The fourisPartOfsites inutils/product-entities.htmlpoint at a different, self-describing embedded object (https://www.pulumi.com/product/#pulumi-platform, with its own inline@type/name) rather than a bare cross-graph@id— so they are not affected by this class of bug and needed no change.graph-builder.html:{{/}}counts go from 103/103 (baseline,origin/master) to 107/107 (this change) — balanced.git diff --statconfirms a single file, 28 insertions, 0 deletions.hugo --minifybuild was attempted for full render verification but fails before reaching any page render, at the CSS asset-fingerprinting step inassets.html— this environment doesn't have the Node asset pipeline built (make ensurerequires Node/Yarn, unavailable here). That failure is unrelated to this change (it's in the CSS pipeline, not schema partials) and pre-exists onorigin/masterin this environment. Full render verification defers to CI (Install deps and build site, test matrix).Impact
isPartOfnow resolves to a real node in their own@graph, instead of a dangling reference.WebSitenode for the first time.WebPagecontainer now declaresisPartOfandinLanguage, completing its identity binding to the site entity.🧠 This PR was created by workprentice on behalf of the Pulumi marketing team's daily technical SEO assignment.