Root cause of the 'docs search is broken entirely' report: deploy-site.yml
fires on every push to main touching website/** (measured 12 deploys in a
day). GitHub Pages keeps only the newest deploy's files, but the CDN chain
(Vercel proxy -> Fastly -> Pages) serves cached HTML for up to ~1 hour
(max-age=300 + stale-while-revalidate=3600). Stale HTML references the
previous deploy's content-hashed bundles, which the new deploy deleted:
sitewide 404s on JS/CSS, dead search (100% client-JS), broken lazy routes
for a large fraction of the day.
Class fix: scripts/retain_pages_assets.py downloads the previous
successful run's github-pages artifact and union-merges its
docs/assets/ + docs/zh-Hans/assets/ into the new tree before upload.
Hashed filenames are content-addressed so collisions are identical;
current build always wins, old files are only added when absent. HTML
and data files are never retained.
Growth bounded by a 7-day retention manifest (docs/assets-retention.json)
carried in the deployed tree. Best-effort: any failure warns and deploys
without retention rather than blocking.
Verified locally: merge logic asserted on a simulated previous tree
(retain old bundle + zh bundle, drop expired entry, never touch HTML,
identical shared chunk untouched).