Documentation site → Cloudflare Workers (fork-safe CI) Implementation Plan
For agentic workers: REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (
- [ ]) syntax for tracking.
Goal: Replace GitHub Pages with Cloudflare Workers static assets (production + per-PR previews), using a secretless Build Site workflow plus a workflow_run Deploy Site workflow with Cloudflare + GitHub Deployment credentials, site-preview / site-production, and an upserted PR comment. Naming uses site throughout (workflows, artifact); the mindmap is the current index.html source only.
Architecture: Build Site runs on pull_request and push to main, checks out the PR head on PRs, produces artifact site with _bundle/public/ + _bundle/worker/ (Worker from that checkout). Deploy Site checks out the default branch (trusted wrangler.toml only), downloads the artifact to _bundle/, copies only public/ and worker/ into cloudflare_site/, runs wrangler deploy on push and wrangler versions upload --preview-alias pr-<pr-number> on pull_request (Wrangler 4.30.0 via cloudflare/wrangler-action@v3.14.1 + wranglerVersion), resolves a workers.dev URL for GitHub, then actions/github-script records Deployments and comments.
Tech Stack: GitHub Actions, Cloudflare Workers (static assets), Wrangler 4.x, cloudflare/wrangler-action@v3.14.1, actions/github-script@v8, REST Deployments API.
Spec: 2026-04-09-site-cloudflare-pages-design.md
File map
| File | Role |
|---|---|
.github/workflows/site-build.yml | Secretless build + artifact site |
.github/workflows/site-deploy.yml | Default-branch checkout + artifact → _bundle/, then copy only public/ + worker/ into cloudflare_site/; wrangler deploy / versions upload, GitHub Deployment + PR comment |
cloudflare_site/wrangler.toml | Worker name placeholder, assets.directory = public, SPA not_found_handling, preview_urls |
cloudflare_site/public/.gitkeep | Keeps public/ in git; CI overwrites with artifact contents |
.github/workflows/mindmap.yml | Removed (replaced by site-build.yml / site-deploy.yml) |
docs/site-deployment.md | Operator runbook: Worker, token scopes (Workers Edit), secrets/variables, fork policy, troubleshooting |
Task 1: Add build workflow
Files:
Create:
.github/workflows/site-build.yml[ ] Step 1: Create the workflow file
Use this exact content (pin actions/checkout to v6.0.2 to match other workflows in this repo):
name: Build Site
on:
pull_request:
push:
branches: [main]
permissions:
contents: read
concurrency:
group: site-build-${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6.0.2
with:
ref: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.sha || github.sha }}
- name: Prepare deploy bundle
run: |
set -euo pipefail
mkdir -p _bundle/public _bundle/worker
cp web/public/index.html _bundle/public/index.html
cp -a cloudflare_site/worker/. _bundle/worker/
- uses: actions/upload-artifact@v4
with:
name: site
path: _bundle/
retention-days: 5- [ ] Step 2: Commit
git add .github/workflows/site-build.yml
git commit -m "ci: add site build workflow for Cloudflare handoff"Task 2: Add deploy workflow
Files:
Create or update:
.github/workflows/site-deploy.yml[ ] Step 1: Implement Deploy Site (canonical copy in repo)
The job must only run for successful runs of this repository’s Build Site workflow, and only for pull_request or push events.
Behavior (Workers, not Pages):
actions/checkouton the default branch (so trustedcloudflare_site/wrangler.tomlexists; deploy does not clone the PR).- Download artifact
siteinto_bundle/, then copy onlypublic/andworker/intocloudflare_site/(so the zip cannot overwritewrangler.toml). push:cloudflare/wrangler-actionwithwranglerVersion: "4.30.0",workingDirectory: site,command: deploy --name=<CLOUDFLARE_PROJECT_NAME>.pull_request: same action withcommand: versions upload --name=<CLOUDFLARE_PROJECT_NAME> --preview-alias pr-<pr-number>(asset config fromcloudflare_site/wrangler.tomlonly; alias falls back topr-<workflow_run.id>if the head matches multiple open PRs).- Resolve URL:
deployment-urloutput, else parse stdout/stderr forworkers.dev. actions/github-script: GitHub Deployments + PR comment;description: Cloudflare Workers (static assets).
Copy the full YAML from the repository file .github/workflows/site-deploy.yml when implementing in another clone.
vars.CLOUDFLARE_PROJECT_NAME: Worker name (same variable name as before).[ ] Step 2: Commit
git add .github/workflows/site-deploy.yml cloudflare_site/wrangler.toml cloudflare_site/public/.gitkeep
git commit -m "ci: deploy site with Workers static assets"Task 3: Remove GitHub Pages workflow
Files:
Delete:
.github/workflows/mindmap.yml(the prior GitHub Pages / mindmap deploy workflow)[ ] Step 1: Delete the file
Remove .github/workflows/mindmap.yml entirely so the site is no longer deployed via actions/deploy-pages.
- [ ] Step 2: Commit
git rm .github/workflows/mindmap.yml
git commit -m "ci: drop GitHub Pages workflow for documentation site"Task 4: Operator runbook
Files:
Create:
docs/site-deployment.md[ ] Step 1: Add the runbook
Create docs/site-deployment.md with the following sections (adjust org/repo names when copying for upstream):
- Overview — Link to the design spec
docs/superpowers/specs/2026-04-09-site-cloudflare-pages-design.mdand summarizeBuild Site/Deploy Site(Workers + static assets). - Cloudflare setup
- Create a Worker (or let first
wrangler deploycreate it); enable preview URLs; optional workers.dev subdomain. - Create an API Token with Cloudflare Workers → Edit (and Account Settings → Read if needed). Pages-only tokens are not sufficient. Store as
CLOUDFLARE_API_TOKEN. - Copy Account ID →
CLOUDFLARE_ACCOUNT_ID. - Add
CLOUDFLARE_PROJECT_NAMEas a GitHub Actions variable = Worker name. - Optional custom domain (fork demos or later
konflux.sh): attach routes on the Worker, not a Pages project.
- Create a Worker (or let first
- GitHub setup (fork — phase 1)
- Repository → Settings → Secrets and variables → Actions:
- Secrets:
CLOUDFLARE_API_TOKEN,CLOUDFLARE_ACCOUNT_ID - Variables:
CLOUDFLARE_PROJECT_NAME
- Secrets:
- Settings → Actions → General → Fork pull request workflows: allow workflows from contributors (so fork PRs can run the build workflow).
- Repository → Settings → Secrets and variables → Actions:
- GitHub setup (upstream — phase 2)
- Same secrets/variables at org or repo level as your governance prefers.
- Confirm the deploy workflow’s
GITHUB_TOKENcan comment on fork PRs (pull-requests: writeis already declared in the workflow). - After cutover, disable GitHub Pages for this repo if it was only used for this site (Settings → Pages).
- Later: attach
konflux.sh(or a subdomain) to the Worker; production URLs in Deployments follow Wrangler output (often*.workers.devuntil custom domain is primary).
- Troubleshooting
- Deploy job skipped: wrong triggering workflow name (must match
Build Siteexactly), orworkflow_run.repositorynot equal to current repo. Could not determine Workers deployment URL: checkwrangler-actionoutputs and Wrangler 4.x stdout/stderr; workflow pins 4.30.0.- Artifact download 404: deploy job needs
actions: readand correctrun-id(already set); build must have uploaded artifactsite. - No PR comment:
workflow_run.pull_requestsempty andpulls.listwithhead=owner:branchdid not return exactly one open PR, or multiple open PRs share the same head (the workflow logs a warning and still deploys a preview; comment upsert is skipped until the head is unique).
- Deploy job skipped: wrong triggering workflow name (must match
- [ ] Step 2: Commit
git add docs/site-deployment.md
git commit -m "docs: add documentation site Cloudflare operator runbook"Task 5: Phase 1 validation (fork)
Files: none (manual)
[ ] Step 1: Configure Cloudflare + GitHub per
docs/site-deployment.mdon your fork.[ ] Step 2: Push a commit on
mainthat touchesweb/public/index.html(document graph; formerlydocs/mindmap.html)
Expected: Build Site succeeds; Deploy Site runs; Cloudflare production Worker updates; GitHub shows site-production with environment_url on workers.dev (or your custom host).
- [ ] Step 3: Open a PR (same repo) (any change that triggers Build Site)
Expected: wrangler versions upload preview; site-preview deployment; one PR comment updated on reruns.
- [ ] Step 4: Open a PR from a second GitHub user / fork (or your own fork of your fork) changing
web/public/index.html
Expected: build succeeds on the base repo without Cloudflare secrets in fork logs; deploy + comment still occur from the base repo’s deploy workflow.
Task 6: Phase 2 — upstream PR
Files: none (manual); branch should contain Tasks 1–4 commits.
[ ] Step 1: Push your branch to origin and open a PR against
konflux-ci/fullsend(or upstream default branch).[ ] Step 2: In the PR description, list maintainer follow-ups: add Actions secrets/variables, verify fork workflow policy, disable legacy GitHub Pages when ready, optional
konflux.shDNS later.[ ] Step 3: After merge, repeat a subset of Task 5 checks on upstream.
Plan self-review
1. Spec coverage
| Spec requirement | Task |
|---|---|
| Cloudflare Workers instead of GitHub Pages | Tasks 2–3 |
Two-phase build + workflow_run deploy | Tasks 1–2 |
site-preview / site-production | Task 2 (createDeployment) |
| PR comment upsert + PR resolution fallback | Task 2 (github-script) |
| PR head checkout | Task 1 |
| Fork-safe (no secrets on build) | Tasks 1 vs 2 permissions |
| Operator docs + phases | Tasks 4–6 |
| Concurrency | Both workflows |
*.workers.dev then konflux.sh on Worker | Task 4 runbook |
2. Placeholder scan
No TBD/TODO left in workflow YAML or task text; cloudflare/wrangler-action@v3.14.1 with wranglerVersion: "4.30.0", github-script v8.
3. Type / naming consistency
- Single artifact name
sitein build and deploy. - Build workflow display name must stay
Build Site— it is theworkflow_run.workflowsfilter target. - Environment names exactly
site-previewandsite-production.
Known follow-up (optional hardening): If createDeployment returns 409 for a rare duplicate ref/environment case, extend the github-script to locate the existing deployment and only create a status (not required for normal one-commit-per-deploy usage).
Plan complete and saved to docs/superpowers/plans/2026-04-09-site-cloudflare-pages.md. Two execution options:
1. Subagent-Driven (recommended) — Dispatch a fresh subagent per task, review between tasks, fast iteration.
2. Inline Execution — Execute tasks in this session using executing-plans, batch execution with checkpoints.
Which approach do you want?
