Skip to content

web-deploy: deploy pipeline crash + Cloudflare build reliability (5 rounds)

Section titled “web-deploy: deploy pipeline crash + Cloudflare build reliability (5 rounds)”

Outcome: KB deploy (kb-business, kb-sdc) fully working again after being silently broken for the whole web-deploy-bug window; the underlying “Cloudflare build failures are invisible” gotcha (recurring since 2026-06-26, 4th occurrence) now has an actual fix instead of another advisory note. All fixes live-verified, not just claimed.

  1. sanitize-kb-source.ts crashed the whole deploy — uncaught PermissionDenied writing to the 12 strategy-lint-generated, deliberately read-only SDC/IP/Strategies/*/index.md files (made read-only 2026-09-21). deploy.sh’s set -e then aborted before sync/transform/push ever ran. Fix: catch Deno.errors.PermissionDenied, warn, skip.
  2. The crash just moved one step later — sync-content.ts’s Deno.copyFile preserves source permission bits, so the read-only files’ local src/content/docs/ copies were read-only too, crashing transform.ts/convert-wikilinks.ts next. Fix: chmod(destPath, 0o644) right after every copy — the protection is for the vault source, not a disposable build copy.
  3. The real reason the site stayed stale — strategy-lint index() never wrote title: frontmatter into its generated index.md files at all. Starlight’s content schema hard-requires title, so the Astro build failed on Cloudflare with InvalidContentEntryDataError — but deploy.sh prints “Push complete!” unconditionally on a successful git push, with zero visibility into the async Cloudflare build’s actual result. Every deploy since 2026-09-21 pushed fine and failed to build; the site was frozen on its last real build (pre-2026-09-17). Fix: strategy_lint.py’s index() now writes proper title: frontmatter into both root and per-group generated files; regenerated all 12.
  4. Cloudflare build reliability, built for real this time (Lesson 10 in web-deploy/LESSONS.md had flagged this exact gap since 2026-06-26 and 3 prior recurrences without ever being built): wait_for_cloudflare_pages_build() in web_deploy.py polls the Cloudflare Pages API for the deployment matching the just-pushed commit and blocks until it reaches a terminal success/failure stage, failing loudly instead of reporting done at push time. Verified both directions (known-good commit → success; known-bad 024bad5 → failure, no hang) before wiring into the real CLI. kb-sdc excluded — its synchronous wrangler pages deploy already returns an accurate result.
  5. A second, unrelated stale-content bug found live in the same session: generate-bases.py picked which static table to render for a \“basequery block from a substring match against the *preceding##heading text* only.Core/DASHBOARD.mdandMBR/DASHBOARD.md's heading ("## Current Tasks/Projects") matched none of the 5 known patterns, so their query blocks shipped as raw YAML instead of a table — indefinitely, with no error. Fix: match the block's own declared name:` field first, heading as fallback.
  • kb-business/sanitize-kb-source.ts, kb-sdc/sanitize-kb-source.ts, kb-mbr/sanitize-kb-source.ts — permission-denied guard.
  • kb-business/sync-content.ts, kb-sdc/sync-content.ts, kb-mbr/sync-content.ts — chmod copies writable.
  • ~/utils/strategy-lint/strategy_lint.py — title: frontmatter on generated index files (no git repo for this util — fix is live on disk, uncommitted, no history to record it in).
  • web_deploy.py — wait_for_cloudflare_pages_build(), wired into execute_deployment(); a CDN-propagation-delay note printed on success; removed a 3-months-dead pre-deploy “Regenerate KB dashboard” step (called a script deleted 2026-06-26, duplicated by generate-bases.py’s own Step 2b) that was the source of the “‘uv’ not found” error Talbot saw.
  • config.yaml — cf_pages_project: kb-online-site (kb-business) / kb-mbr (mothballed); dropped dead run_kb_dashboard flag everywhere.
  • .env — added CLOUDFLARE_ACCOUNT_ID (token already present).
  • kb-business/generate-bases.py — match Bases views by declared name:, not heading substring.
  • ~/ai-config/AGENTS.md — new Hard Rule: verify async external state (Cloudflare/CI builds, webhooks) programmatically rather than handing a timing-sensitive check to a human’s eyes. Deployed via the post-commit hook, confirmed.
  • Core/Processes/Software Dev/GlobalDevRules.md §3.13 — addendum: a read-only generated file’s protection doesn’t survive being copied by a build/sync pipeline; chmod the copy writable at the copy site.
  • web-deploy/LESSONS.md — Lesson 10 updated from “not yet built” to built-and-shipped; new Lesson 12 on matching content by declared identity over free-form prose.
  • Both known-good and known-bad Cloudflare commits tested directly against wait_for_cloudflare_pages_build() before it was wired into the CLI.
  • Ran the real web-deploy kb-business online and web-deploy kb-sdc online commands (not just the underlying scripts) twice each across the fix iterations; both completed with Cloudflare build success confirmed via the API.
  • Fresh curl (no cache) of both live sites after the final round: zero occurrences of pre-cleanup stray content, /apps///archive/ 404, Core/DASHBOARD.md and MBR/DASHBOARD.md render real tables (no raw YAML).
  • Full local Astro build run twice (before and after the Bases-matching fix): 1235 pages, exit 0 both times.

~/utils/strategy-lint/ has no git repository — the title: frontmatter fix is live on disk (plain Python, takes effect immediately) but has no commit history. Worth a git init there if the tool keeps growing (Talbot’s call, not applied unilaterally this session).

  • web-deploy (~/utils/web/web-deploy/): 1327a51, fdf24cd, f5b3978, 48d31d5, 7bfcd5c
  • web-deploy/kb-business: b3222af, f426523, 8d8f568
  • web-deploy/kb-sdc: 4edd88d, befeb00
  • web-deploy/kb-mbr: 7a94c5c, 46e9177
  • ai-config: f4af379

Closed as terminal — all 3 original asks plus 2 reliability fixes plus 1 newly-found bug are fixed and live-verified; no further web-deploy work currently identified.