Published pages audit — 2026-09-06¶
The first run of frontend/scripts/audit-published-pages.mjs (issue #361) against production: every URL in the live sitemap, in headless Chromium, one verdict each. The crawl re-runs weekly from .github/workflows/audit-published-pages.yml; this page is the dated record of what it found the first time and where each finding went.
- Base:
https://hybridcloudworks.com· sitemap entries: 120 (2 duplicate, dropped) · URLs crawled: 118 · started 2026-09-06T05:32Z - Verdicts: works 0, empty 11, defect 107
What "defect" mostly means here¶
One root cause accounts for almost all of it: every prerendered page ships a pending Suspense boundary, and the Content Security Policy blocks the two inline scripts React emits to complete it, so hydration reports error 419 on 107 of 118 pages (#370). Take that class away and the site has a handful of page-specific problems, each with its own issue below.
The live sitemap itself carried one URL three times (/azure/blog/enable-ai-powered-discovery-of-azure-updates-with-microsoft-release-communicatio); the crawler de-duplicates and reports the count, and the repeat is noted on #373 with the other sitemap hygiene items.
Finding classes¶
| Class | Pages | Where it went |
|---|---|---|
console csp-inline-script |
107 | #370 |
console react-hydration-419 |
107 | #370 |
thin main region |
7 | #373 |
console network-failed |
5 | #371 |
broken images |
5 | #371 / #374 |
failed requests |
4 | #371 |
empty-state copy |
4 | by configuration |
console csp-other |
2 | #370 |
media not served |
2 | #372 |
title lacks provider name |
1 | note |
og:title lacks provider name |
1 | note |
Child issues¶
-
370 — pending Suspense boundary in every prerender + CSP blocks the completion scripts (site-wide, P1)¶
-
371 — landing hero image sets missing for GCP, GitHub, Terraform, FinOps (P2)¶
-
372 — podcast rows with dead PodBean media still served (P2)¶
-
373 — seven section pages render empty and sit in the sitemap (frameworks for Azure, AWS, GCP, FinOps and VMware; Ansible code and coder-corner); one sitemap URL repeated (P3)¶
-
374 — curated article bodies hotlink upstream images that no longer load (P3)¶
Empty by configuration, not defects¶
/tools/migration,/tools/comparison,/tools/resources,/tools/decisions— "Coming Soon" by design./aws/audio,/gcp/audio,/vmware/audio— no podcast feed for those providers until #349./about— the two/data/*.jsonsnapshots 404 by an accepted decision (#175); the page falls back to the API. Recorded as a note, not a finding.
Re-run after the fixes, same day¶
Actions run 34023953833, after #376 (prerender inline), #379 (dead media hidden) and #380 (hero references) were deployed at 09:09 UTC:
| Verdict | First run | After deploy |
|---|---|---|
| works | 0 | 68 |
| empty | 11 | 48 |
| defect | 107 | 2 |
The two remaining defects are /azure/architecture-designs (title lacks the
provider name) and one curated article with a hotlinked image (#374). The
hydration and CSP classes are gone. The empty count rose because the
hydration class no longer masks it: roughly thirty section pages have no
published content behind them (blog, coder-corner, frameworks and code for
most providers), which #373 now records in full, and three landings were
marked empty on a podcast widget's copy alone — a heuristic corrected in the
crawler after this run (empty-state copy makes a page empty only when its
main region is under 800 characters; on a fuller page it is recorded as a
note).
Re-run after the batch of 2026-09-07¶
Actions run 34111155605, against the site as deployed that morning with #386,
388, #389, #390, #391, #392, #395, #396 and the refreshed content manifest¶
(#399):
| Verdict | First run | After 09-06 deploy | After 09-07 deploy |
|---|---|---|---|
| works | 0 | 68 | 70 |
| empty | 11 | 48 | 40 |
| defect | 107 | 2 | 3 |
The sitemap itself went from 118 URLs to 113 with zero duplicates: the eight
/<provider>/frameworks pages left it, because the manifest now counts the
published items per section and the pre-render omits a section with none
(#373), and the blog URL that appeared three times appears once (#386).
The empty count fell by eight for the same reason — those pages are no longer advertised, so they are no longer crawled — and the pages that remain empty now say so in the site's own words rather than inviting the reader to clear filters that were not the cause.
Three defects, none of them the two from the previous run:
| Page | Finding | Disposition |
|---|---|---|
/ |
net::ERR_ABORTED on public/platform-health |
Not a defect. The route answered 200 with a valid body when asked directly a minute later; Chromium reports a request the browser cancelled the same way it reports one the network failed. The crawler now ignores that error text. |
/azure/architecture-designs |
title and og:title lack the provider name | Fixed: the page now titles itself Azure Architecture Designs, matching the AWS page it was copied from. |
/azure/blog/microsoft-foundry-end-to-end-observability-and-roi-for-production-ai-agents |
one broken image | Fixed in code, and it was not in the body. The broken <img> was the hero, resolved from contentImageUrl, which the curated feed had filled with …/2026/06/RUBRIC-EVALUATOR.mp4 — a real 5.3 MB video/mp4. The body was never involved: the field that renders is the RSS stub in Content, which carries no images, and the .mp4 appears in the unrendered content field only as a markdown link, which is fine. So re-hosting (#374) was never going to reach it. Cover resolution now rejects a URL whose media type is video (isVideoUrl in frontend/src/lib/blogUtils.js) and takes the next candidate instead, so the hero is omitted, og:image and twitter:image stop advertising a video to crawlers, and twitter:card falls back to summary. The check is on the extension or declared media type, not on that one URL, because the same feed will do this again. |
The hero images this run confirms serving: /gcp, /github, /terraform and
/finops all answer 200 for 1.png (#371, #390).
Where the empty pages ended, 2026-09-07¶
The empty column above is what the crawler saw. What matters for #373 is narrower: how many empty pages the sitemap still advertises, because a page that renders an honest empty state and is not crawled is not a defect.
| Sitemap URLs | Empty pages in it | |
|---|---|---|
| Before any of this work | 118 (5 duplicated) | 11 |
| After #386 and the first half of #373 | 113 | 48 |
| After #404 — section counts move into the app | 80 | 7 |
| After #417 — architecture counted, once deployed | 79 | 6 |
The last two rows are one route, /vmware/architecture-designs, and it took a
separate change because the obvious one was wrong. #404 deferred it on the
grounds that all five architecture pages merge staticBlueprints hardcoded in
the page, so an API count of zero is true of every one of them and right about
only one. That reasoning was correct about the risk. It was wrong about the
data: frontend/src/pages/vmware/ArchitecturePage.jsx had no
staticBlueprints at all, and is the only one of the five that can be empty.
417 makes the pre-render derive that rather than declare it. Each page's¶
list moved to a sibling architecture-blueprints.js, VMware's exporting [],
and sitemapRoutes drops a route only when the API count is zero, the
unattributed bucket is zero, and that page's own module is empty. A
hardcoded list of "pages that have static content" would have been wrong the
first time someone added a blueprint, with nothing to say so.
The control build is the evidence that the earlier caution was justified: with
the same counts and the pre-#417 rule, /aws, /finops and /gcp
architecture-designs leave the sitemap as well — 76 URLs instead of 79. Three
working pages unadvertised to fix one empty one, measured rather than argued.
The six that remain are empty by configuration and stay. Four /tools/*
pages say Coming Soon because nothing has been written for them yet, and two
/templates/* pages are submission forms the crawler reads as thin because a
form has little prose. Neither is a page that lost its content.
Nothing above is live yet. Merging #417 changed no URL; the sitemap moves only after a Functions deploy carries the new counts, the manifest workflow republishes them, and a frontend deploy rebuilds — the same sequence #404 needed. The live sitemap is 80 URLs until then.
Matrix¶
| Path | HTTP | Verdict | Findings |
|---|---|---|---|
/ |
200 | defect | console csp-inline-script; console react-hydration-419 |
/about |
200 | defect | console csp-inline-script; console react-hydration-419; console network-failed; note: #175 data snapshot |
/contact |
200 | defect | console csp-inline-script; console react-hydration-419 |
/azure |
200 | defect | console csp-inline-script; console react-hydration-419 |
/azure/blog |
200 | defect | console csp-inline-script; console react-hydration-419 |
/azure/architecture-designs |
200 | defect | console csp-inline-script; console react-hydration-419; title lacks provider name: "Architecture Designs | Hybrid Cloud Works"; og:title lacks provider name: "Architecture Designs | Hybrid Cloud Works" |
/azure/frameworks |
200 | empty | thin main region: 284 chars |
/azure/coder-corner |
200 | defect | console csp-inline-script; console react-hydration-419 |
/azure/education |
200 | defect | console csp-inline-script; console react-hydration-419 |
/azure/audio-architecture |
200 | defect | console csp-inline-script; console react-hydration-419; console csp-other; media not served: 404 |
/azure/audio |
200 | defect | console csp-inline-script; console react-hydration-419; console csp-other; media not served: 404 |
/azure/news |
200 | defect | console csp-inline-script; console react-hydration-419 |
/azure/rss |
200 | defect | console csp-inline-script; console react-hydration-419 |
/aws |
200 | defect | console csp-inline-script; console react-hydration-419 |
/aws/blog |
200 | defect | console csp-inline-script; console react-hydration-419 |
/aws/architecture-designs |
200 | defect | console csp-inline-script; console react-hydration-419 |
/aws/frameworks |
200 | empty | thin main region: 280 chars |
/aws/coder-corner |
200 | defect | console csp-inline-script; console react-hydration-419 |
/aws/education |
200 | defect | console csp-inline-script; console react-hydration-419 |
/aws/audio-architecture |
200 | defect | console csp-inline-script; console react-hydration-419 |
/aws/audio |
200 | defect | console csp-inline-script; console react-hydration-419 |
/aws/news |
200 | defect | console csp-inline-script; console react-hydration-419 |
/aws/rss |
200 | defect | console csp-inline-script; console react-hydration-419 |
/gcp |
200 | defect | console csp-inline-script; console network-failed; console react-hydration-419; failed requests: 7; broken images: 6 |
/gcp/blog |
200 | defect | console csp-inline-script; console react-hydration-419 |
/gcp/architecture-designs |
200 | defect | console csp-inline-script; console react-hydration-419 |
/gcp/frameworks |
200 | empty | thin main region: 280 chars |
/gcp/coder-corner |
200 | defect | console csp-inline-script; console react-hydration-419 |
/gcp/education |
200 | defect | console csp-inline-script; console react-hydration-419 |
/gcp/audio-architecture |
200 | defect | console csp-inline-script; console react-hydration-419 |
/gcp/audio |
200 | defect | console csp-inline-script; console react-hydration-419 |
/gcp/news |
200 | defect | console csp-inline-script; console react-hydration-419 |
/gcp/rss |
200 | defect | console csp-inline-script; console react-hydration-419 |
/github |
200 | defect | console csp-inline-script; console network-failed; console react-hydration-419; failed requests: 7; broken images: 6 |
/github/blog |
200 | defect | console csp-inline-script; console react-hydration-419 |
/github/code |
200 | defect | console csp-inline-script; console react-hydration-419 |
/github/coder-corner |
200 | defect | console csp-inline-script; console react-hydration-419 |
/github/education |
200 | defect | console csp-inline-script; console react-hydration-419 |
/github/audio-architecture |
200 | defect | console csp-inline-script; console react-hydration-419 |
/github/audio |
200 | defect | console csp-inline-script; console react-hydration-419 |
/github/news |
200 | defect | console csp-inline-script; console react-hydration-419 |
/github/rss |
200 | defect | console csp-inline-script; console react-hydration-419 |
/terraform |
200 | defect | console csp-inline-script; console network-failed; console react-hydration-419; failed requests: 7; broken images: 6 |
/terraform/blog |
200 | defect | console csp-inline-script; console react-hydration-419 |
/terraform/code |
200 | defect | console csp-inline-script; console react-hydration-419 |
/terraform/coder-corner |
200 | defect | console csp-inline-script; console react-hydration-419 |
/terraform/education |
200 | defect | console csp-inline-script; console react-hydration-419 |
/terraform/audio-architecture |
200 | defect | console csp-inline-script; console react-hydration-419 |
/terraform/audio |
200 | defect | console csp-inline-script; console react-hydration-419 |
/terraform/news |
200 | defect | console csp-inline-script; console react-hydration-419 |
/terraform/rss |
200 | defect | console csp-inline-script; console react-hydration-419 |
/finops |
200 | defect | console csp-inline-script; console network-failed; console react-hydration-419; failed requests: 7; broken images: 6 |
/finops/blog |
200 | defect | console csp-inline-script; console react-hydration-419 |
/finops/architecture-designs |
200 | defect | console csp-inline-script; console react-hydration-419 |
/finops/frameworks |
200 | empty | thin main region: 286 chars |
/finops/coder-corner |
200 | defect | console csp-inline-script; console react-hydration-419 |
/finops/education |
200 | defect | console csp-inline-script; console react-hydration-419 |
/finops/audio-architecture |
200 | defect | console csp-inline-script; console react-hydration-419 |
/finops/audio |
200 | defect | console csp-inline-script; console react-hydration-419 |
/finops/news |
200 | defect | console csp-inline-script; console react-hydration-419 |
/finops/rss |
200 | defect | console csp-inline-script; console react-hydration-419 |
/vmware |
200 | defect | console csp-inline-script; console react-hydration-419 |
/vmware/blog |
200 | defect | console csp-inline-script; console react-hydration-419 |
/vmware/architecture-designs |
200 | defect | console csp-inline-script; console react-hydration-419 |
/vmware/frameworks |
200 | empty | thin main region: 286 chars |
/vmware/coder-corner |
200 | defect | console csp-inline-script; console react-hydration-419 |
/vmware/education |
200 | defect | console csp-inline-script; console react-hydration-419 |
/vmware/audio-architecture |
200 | defect | console csp-inline-script; console react-hydration-419 |
/vmware/audio |
200 | defect | console csp-inline-script; console react-hydration-419 |
/vmware/news |
200 | defect | console csp-inline-script; console react-hydration-419 |
/vmware/rss |
200 | defect | console csp-inline-script; console react-hydration-419 |
/ansible |
200 | defect | console csp-inline-script; console react-hydration-419 |
/ansible/blog |
200 | defect | console csp-inline-script; console react-hydration-419 |
/ansible/code |
200 | empty | thin main region: 169 chars |
/ansible/coder-corner |
200 | empty | thin main region: 197 chars |
/ansible/education |
200 | defect | console csp-inline-script; console react-hydration-419 |
/ansible/audio-architecture |
200 | defect | console csp-inline-script; console react-hydration-419 |
/ansible/audio |
200 | defect | console csp-inline-script; console react-hydration-419 |
/ansible/news |
200 | defect | console csp-inline-script; console react-hydration-419 |
/ansible/rss |
200 | defect | console csp-inline-script; console react-hydration-419 |
/finops/tools |
200 | defect | console csp-inline-script; console react-hydration-419 |
/finops/focus |
200 | defect | console csp-inline-script; console react-hydration-419 |
/finops/architectures |
200 | defect | console csp-inline-script; console react-hydration-419 |
/terraform/modules |
200 | defect | console csp-inline-script; console react-hydration-419 |
/terraform/tools |
200 | defect | console csp-inline-script; console react-hydration-419 |
/github/workflows |
200 | defect | console csp-inline-script; console react-hydration-419 |
/github/tools |
200 | defect | console csp-inline-script; console react-hydration-419 |
/tools/migration |
200 | empty | empty-state copy: Coming Soon | Check back soon for updates. |
/tools/comparison |
200 | empty | empty-state copy: Coming Soon | Check back soon for updates. |
/tools/resources |
200 | empty | empty-state copy: Coming Soon | Check back soon for updates. |
/tools/decisions |
200 | empty | empty-state copy: Coming Soon | Check back soon for updates. |
/templates/framework |
200 | defect | console csp-inline-script; console react-hydration-419 |
/templates/architecture |
200 | defect | console csp-inline-script; console react-hydration-419 |
/templates/blog |
200 | defect | console csp-inline-script; console react-hydration-419 |
/templates/coder-corner |
200 | defect | console csp-inline-script; console react-hydration-419 |
/templates/rosetta-stone |
200 | defect | console csp-inline-script; console react-hydration-419 |
/azure/blog/azure-foundry-hosted-agents-accelerating-ai-agent-deployment-optimization-featur |
200 | defect | console csp-inline-script; console react-hydration-419 |
/azure/blog/enable-ai-powered-discovery-of-azure-updates-with-microsoft-release-communicatio |
200 | defect | console csp-inline-script; console react-hydration-419 |
/azure/blog/enterprise-hub-and-spoke-zero-trust-edge |
200 | defect | console csp-inline-script; console react-hydration-419 |
/azure/blog/operationalizing-azure-updates-with-microsoft-release-communications-and-proacti |
200 | defect | console csp-inline-script; console react-hydration-419 |
/azure/blog/autonomous-self-healing-for-azure-vmware-solution-private-clouds |
200 | defect | console csp-inline-script; console react-hydration-419 |
/azure/blog/microsoft-foundry-end-to-end-observability-and-roi-for-production-ai-agents |
200 | defect | console csp-inline-script; console react-hydration-419; broken images: 2 |
/azure/blog/leveraging-ai-for-efficient-azure-update-discovery-with-microsoft-release-commun |
200 | defect | console csp-inline-script; console react-hydration-419 |
/azure/blog/unveiling-microsofts-secure-future-initiative-sfi-a-blueprint-for-next-generatio-91IWYg |
200 | defect | console csp-inline-script; console react-hydration-419 |
/azure/blog/unlocking-on-prem-genai-with-azure-arc-agentic-retrieval-in-foundry-local |
200 | defect | console csp-inline-script; console react-hydration-419 |
/azure/blog/discover-and-assess-file-shares-for-migration-to-azure-files-with-azure-migrate |
200 | defect | console csp-inline-script; console react-hydration-419 |
/azure/blog/new-microsoft-certified-azure-ai-fundamentals-certification |
200 | defect | console csp-inline-script; console react-hydration-419 |
/azure/blog/a-developers-guide-to-managing-models-cost-and-quality-in-microsoft-foundry |
200 | defect | console csp-inline-script; console react-hydration-419 |
/azure/blog/new-microsoft-certified-sql-ai-developer-associate-certification |
200 | defect | console csp-inline-script; console react-hydration-419 |
/azure/blog/announcing-the-new-microsoft-certified-azure-ai-apps-and-agents-developer-associ |
200 | defect | console csp-inline-script; console react-hydration-419 |
/azure/blog/leveraging-ai-powered-discovery-for-azure-updates-with-microsoft-release-communi |
200 | defect | console csp-inline-script; console react-hydration-419 |
/azure/blog/ansible-azure-arc-use-ansible-modules-to-deploy-and-manage-azure-arc-machine-ext |
200 | defect | console csp-inline-script; console react-hydration-419 |
/azure/blog/announcing-private-preview-deploy-ansible-playbooks-using-azure-policy-via-machi |
200 | defect | console csp-inline-script; console react-hydration-419 |
/azure/blog/automating-arc-enabled-sql-server-license-type-configuration-with-azure-policy |
200 | defect | console csp-inline-script; console react-hydration-419 |
/aws/blog/aws-transform-enhances-migration-assessments-with-new-agentic-capabilities-IVrBFJ |
200 | defect | console csp-inline-script; console react-hydration-419 |
/azure/blog/nist-vs-microsoft-zero-trust-why-cloud-architects-should-care-LSzyB6 |
200 | defect | console csp-inline-script; console react-hydration-419 |
/azure/blog/microsoft-sovereign-cloud-unleashing-disconnected-operations-with-enhanced-gover-Ttt3Hv |
200 | defect | console csp-inline-script; console react-hydration-419 |
/azure/blog/microsoft-sovereign-cloud-empowering-europe-with-unprecedented-data-control-and--ibDYCO |
200 | defect | console csp-inline-script; console react-hydration-419 |