Third track of a multi-track CONV-30 day. The earlier
/handoffin this same conversation closed the buyer-journey track (CONV-30-buyer-journey.md). After that, Roman asked to "actually connect with Cloudflare and ensure whatever we are creating, like to view, is actually running on an actual online so that we can share it between myself and Sergei, but also with any external parties we want." This file documents everything from that ask onward.
Resume Prompt
Cloudflare deploy is live at https://preview.offplan.online/ — any HTML committed to
docs/rendered/on the os repo auto-deploys via the GitHub Action (once API token secrets are added) or viawrangler pages deploy docs/rendered/ --project-name=offplan-preview --branch=mainmanually.CLAUDE.md§ "Live preview at preview.offplan.online" documents the convention. First action next session: addCLOUDFLARE_API_TOKEN+CLOUDFLARE_ACCOUNT_IDrepo secrets in GitHub (offplan-online/os → Settings → Secrets and variables → Actions), then test by pushing a trivial change todocs/rendered/and watching the Action deploy. Also: clean up the straycf-*.png/github-*.pngdebug screenshots inos/root (left over from the Playwright-driven dashboard attempt).
Summary
This stretch took the repo from "HTML renders exist locally" to "HTML renders are live at a shareable HTTPS URL on a custom domain." The end state: https://preview.offplan.online/ serves the contents of docs/rendered/ from Cloudflare Pages, with a paired https://offplan-preview.pages.dev/ backup URL. An auto-generated index page lists every render latest-first using the canonical bundle visual register so the entry surface feels brand-consistent. A GitHub Action wired via cloudflare/wrangler-action@v3 redeploys on every push to main that touches docs/rendered/**, plans/**.md, workstreams/**.md, or the index generator script — but is one step short of running unattended (two repo secrets still need to be added; ~2 min human action).
The shape of the work split cleanly in two: investigation + architecture choice (single-repo vs two-repo; single-repo won) and execution (which started in the Cloudflare dashboard via Playwright and pivoted to wrangler CLI when a dashboard UI bug blocked progress). The dashboard's GitHub repo-connection dropdown only surfaces personal accounts — even after installing the Cloudflare GitHub App on the offplan-online org and re-running the OAuth flow, the org never appeared in the dropdown. Multiple re-install / re-OAuth / fresh-dropdown iterations all failed at the same step. Pivoting to wrangler pages project create + wrangler pages deploy worked first try. Custom domain provisioning via the Cloudflare dashboard's Custom domains tab (a different surface) worked fine — only the repo-selection step is buggy.
Beyond the deploy itself, this stretch also closed out the offplan-online/preview GitHub repo (archived, no longer needed under the single-repo architecture), added the live-preview convention to CLAUDE.md for future Sergei/Roman onboarding parity, and added a project-scoped .gitignore line for wrangler's local cache.
Changes
Live deploy
- Cloudflare Pages project
offplan-previewcreated viawrangler pages project create offplan-preview --production-branch=main(Account:[email protected], IDb16caee7481d783c292d26ec76782cb1). - First deploy via
wrangler pages deploy docs/rendered/ --project-name=offplan-preview --branch=main --commit-hash=$COMMIT_HASH --commit-message="$COMMIT_MSG"— succeeded on first try after the dashboard pivot. - Custom domain
preview.offplan.onlineadded via the Cloudflare Pages dashboard Custom domains tab. CNAMEpreview→offplan-preview.pages.devprovisioned on theoffplan.onlinezone. SSL active. - Live URLs:
- https://preview.offplan.online/ (production custom domain)
- https://offplan-preview.pages.dev/ (Cloudflare default backup URL)
Index page generator
scripts/build-rendered-index.py(NEW) — Python generator. Sorts entries latest-first by git commit time (fallback to mtime if not tracked), adds aLATESTchip on the newest entry, uses the canonical bundle visual register (Skeleton White surface, Helvetica Neue 200/300 display, Inter body, JetBrains Mono mono). Output written todocs/rendered/index.html.docs/rendered/index.html(NEW) — initial index, committed alongside the generator so the first deploy has an entry surface.
Auto-deploy automation
.github/workflows/deploy-rendered.yml(NEW) — usescloudflare/wrangler-action@v3. Triggers on push tomainwhen any of:docs/rendered/**,plans/**.md,workstreams/**.md,scripts/build-rendered-index.pychange. Regenerates the index, then runswrangler pages deploy.- Required secrets (NOT YET ADDED):
CLOUDFLARE_API_TOKEN— Roman creates at https://dash.cloudflare.com/profile/api-tokens using the "Edit Cloudflare Workers" template (includes Pages permissions).CLOUDFLARE_ACCOUNT_ID=b16caee7481d783c292d26ec76782cb1
- Until those secrets are added, the Action will fail at the deploy step. Manual
wrangler pages deploy …continues to work locally from Roman's terminal (he ranwrangler loginduring this stretch).
Convention added to CLAUDE.md
- New section "Live preview at preview.offplan.online" in
CLAUDE.md— documents the architecture (Cloudflare Pages project name, account ID, source path, trigger paths, index generator), the convention ("every complex .md plan or workstream gets a paired .html render committed todocs/rendered/"), the canonical visual register reference, and the manual-deploy fallback command. Notes theoffplan-online/previewrepo retirement.
Repo housekeeping
.gitignore— added.wrangler/(wrangler's local project cache, regenerated on everywranglerinvocation).offplan-online/previewGitHub repo archived viagh repo archive offplan-online/preview --yes. No longer needed under the single-repo architecture (Cloudflare now deploys fromosdirectly with path filtering).
Commits this session-tail (Cloudflare stretch)
e7a4cc9feat: docs/rendered/index.html auto-generator + initial index608f2c1feat: Cloudflare Pages auto-deploy to preview.offplan.online9d941dfchore: gitignore .wrangler/
Earlier today, before the explicit Cloudflare ask, these CONV-30 commits also landed under the buyer-journey track (separately recorded in CONV-30-buyer-journey.md):
1f26e1eplan: sales-app-react module sequence ratified859d0a9chore: rename docs/plan/ → docs/rendered/bdeda17assets: design-system bundle + un-gitignore Path 1/2 imagerya4daf30chore: Sergei parity — project-scoped skills + commands + ADRsc984875fix: replace skill symlinks with real contentbc14d3cdocs: SETUP_SERGEI.md- Plus an inlined Perplexity API key in
.mcp.json(Roman explicitly OK'd team-share). - Plus a mid-CONV
path2-*→sales-app-react-*rename across plan + workstreams.
What's Next
- Roman adds the two GitHub repo secrets (~2 min, one-time):
- Create Cloudflare API token at https://dash.cloudflare.com/profile/api-tokens (template: "Edit Cloudflare Workers" — includes Pages permissions).
- Add to
offplan-online/os→ Settings → Secrets and variables → Actions:CLOUDFLARE_API_TOKEN= (the new token)CLOUDFLARE_ACCOUNT_ID=b16caee7481d783c292d26ec76782cb1
- Test by pushing a trivial change under
docs/rendered/and watching the Action tab.
- Clean up stray debug screenshots in
os/root:cf-*.pngandgithub-*.png(Playwright captures from the dashboard-driving attempt)..playwright-mcp/is already gitignored but the root-level screenshots aren't. Either delete or add the patterns to.gitignore.
Open Questions
- Cloudflare dashboard UI bug. The Pages "Connect to Git → Select GitHub account/org → Select repository" dropdown filters by personal account only; orgs you have access to (even with the Cloudflare GitHub App installed on the org) don't appear. Not filed upstream anywhere. Worth a note in
SETUP_SERGEI.mdso Sergei doesn't bang his head against the same problem if he ever needs to set up another Pages project — the wrangler CLI path is the reliable workaround. - Brand workstream (
brand-language-and-identity.md) is still a stub awaiting its own/plansession per ADR 0015 + 0016. Separate from the Cloudflare work but mentioned here for context — the brand work is one of the two parallel tracks unblocked by the buyer-journey ratification, and the live-preview surface now exists to host its outputs once renders start landing.
Key Context
- Single-repo decision. Considered two-repo (
osplanning +previewstatic-hosting) vs single-repo (justoswith output-dir filtering at the deploy edge). Roman picked single-repo: simpler mental model, one git history, the deploy edge handles the public/private filtering. Trade-off accepted: anything indocs/rendered/is public, the rest ofos(plans/, workstreams/, sessions/,.mcp.jsonwith the Perplexity team key,.env, memory references) stays private in git. - Wrangler-vs-dashboard fallback story. Attempted Cloudflare Pages setup via the dashboard first (Playwright-driven). Hit the repo-dropdown UI bug. Multiple workaround iterations (re-install Cloudflare GitHub App on the org, re-run OAuth, fresh dropdowns) all failed at the same step. Pivoted to
wrangler login+wrangler pages project create+wrangler pages deploy— worked first try. Custom domain provisioning via the dashboard's Custom domains tab (a different surface) worked fine. Lesson for future infra work: the wrangler CLI is the reliable path; the dashboard is for surfaces wrangler doesn't expose (like custom domains). - CONV-30 collision history. This is the third file carrying the CONV-30 label today. Roman's parallel terminal claimed CONV-30 first (Stage 1 plan track,
CONV-30.md, 2026-05-09). The buyer-journey track self-labelled CONV-30 too, mid-session, without noticing (CONV-30-buyer-journey.md). This Cloudflare stretch inherited the same label by virtue of being a continuation of the same conversation. Filename suffixes (-buyer-journey,-cloudflare) are the disambiguator; in-content references all carry the bareCONV-30label as committed. Next session should be CONV-35 (highest used across all tracks + 1). - The post-commit hook auto-pushes. Each of the three commits above pushed cleanly to
main.