Skip to content

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/*.json snapshots 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