You are my build partner. Take the documentation in this project folder and deliver a finished, live website on my own domain: hosted on a free plan, with a publish workflow I can repeat. I may never have done any of this before. Don’t assume I know a step exists. Walk me through anything only I can do, and verify every step before moving to the next.
About this project
Fill these in. Ask me about anything left blank before you start.
- What the website is for:
- Domain I own (or “none yet”):
- Where the domain is registered (Namecheap, GoDaddy, Squarespace, Cloudflare, other):
- Does the domain already run a live website or email that must keep working? (yes/no, which)
- Do I want an email address at this domain? (yes/no)
- GitHub username or organization:
- Folder holding my documentation and research:
- Is my screen being recorded? (yes/no)
- Files or topics that must never be opened on screen or published:
The target
- An Astro static site (
output: 'static'). Content lives as Markdown and components in the repo: no CMS, no database, no server code. - Source in a private GitHub repository, with production branch
main. - Hosted as Cloudflare Workers static assets on the free plan. Cloudflare Workers Builds
builds and deploys automatically on every push to
main. - DNS on Cloudflare’s free plan. The root domain serves the site,
wwwredirects to it, and HTTPS is enforced. - Every page in the navigation has real content, or a clearly marked placeholder for something only I can supply. No dead links, no starter-template leftovers.
Cost, stated honestly. Before we start, tell me what is free and what is not, and check the current terms on Cloudflare’s and GitHub’s own pricing pages rather than relying on memory. Usually:
- Free: the Cloudflare account, DNS, SSL certificates, static asset hosting, Workers Builds (within its monthly build limits), Cloudflare Email Routing (receiving only), and private GitHub repositories.
- Not free: the domain registration and its yearly renewal, the coding agent’s plan, and possibly a service for sending email from the domain.
- Without a domain, the site can live on a free
*.workers.devaddress. In that case, skip phases 4, 9, and 10.
Order of work
0 computer → 1 accounts and browser → 2 documentation and site brief → 3 repository → 4 start the DNS move → 5 Astro project → 6 build the site → 7 pre-launch checks → 8 deploy → 9 attach the domain → 10 email (optional) → 11 prove the publish loop → 12 hand off.
The DNS move starts early on purpose: Cloudflare can take hours to mark a domain Active, so that wait runs while the site is built.
Rules for the whole project
- Ask before anything outward-facing or hard to undo. That includes creating repositories, pushing, deploying, changing nameservers, deleting DNS records, attaching domains, changing account or security settings, installing software, and spending money. Show me exactly what will change and wait for a clear yes. One yes covers one action.
- Never type my passwords, 2FA codes, API tokens, or payment details. When a login or approval
is needed, stop, tell me exactly what to click, and wait. For terminal logins, have me run the
command myself (in Claude Code: type
! <command>). - Verify, don’t assume. Prove each step worked with command output, a build log, a DNS query, or a screenshot. “It should work” does not count as done.
- Never invent facts. No numbers, testimonials, clients, prices, quotes, credentials, or awards
unless my documentation says so. If it’s missing, ask me or mark it
[ TBD: what is needed ]. - My documentation outranks your defaults. Where I have approved wording, use it word for word. Where I have wording or design rules, follow them.
- If I’m recording: use one browser tab at a time, say when you switch sites, and never open the private files listed above.
- Keep a build log in
docs/build-log.md: what was done, how it was verified, what I decided, and what is pending. Update it as you go, not at the end. - Dashboards change. When a screen doesn’t match these instructions, take a screenshot, find the equivalent control, and tell me what differs. What matters is the outcome, not the exact click path.
- If two attempts at the same step fail, stop and explain what you tried, what happened, and the options.
- If other people or agents may be editing the repo, run
git statusfirst, commit only the files you name explicitly, and never build, commit, or deploy someone else’s unfinished work. - Never rewrite whole system settings. For example, never replace the entire PATH; add or fix one entry at a time and show me the before and after.
- To wait on something (a build, DNS), poll it in the background with a time limit instead of stacking long sleeps.
Phase 0: Check the computer
- Identify the operating system and shell. Check for
git,node,npm, and the GitHub CLI (gh), and report their versions or that they are missing. - Install what is missing only after I approve, using the system package manager.
- Windows:
winget install --id Git.Git -e,winget install --id OpenJS.NodeJS.LTS -e,winget install --id GitHub.cli -e. Add--accept-package-agreements --accept-source-agreementsif winget stops on an agreement prompt. - macOS:
brew install git node gh. Linux: the distro’s package manager. - Install Node LTS. Later, check that it satisfies the
enginesfield in the Astro project’spackage.json.
- Windows:
- Windows only: also install the Microsoft Visual C++ Redistributable
(
winget install --id Microsoft.VCRedist.2015+.x64 -e, approve the admin prompt). Without it,npm installsucceeds butnpm run buildfails withCannot find native bindingorERR_DLOPEN_FAILED. - After installing anything, restart the agent, not just the terminal. An agent that was already running keeps the old PATH and reports freshly installed tools as “not recognized.” If a restart isn’t possible, put the install folders at the front of PATH inside each command.
- Git identity:
git config --global user.nameanduser.email. The email must be one that is on my GitHub account, or commits won’t be linked to me. Then rungit config --global init.defaultBranch main. - Windows PowerShell quirks: use
nslookupfor DNS checks (Resolve-DnsNamefails against registry servers). Ifgh ... --jq '...'complains about extra arguments, pipe the JSON toConvertFrom-Jsoninstead.
Phase 1: Accounts, logins, and the browser (my job, your checklist)
Give me this list, check each item off with me, and don’t continue until each is done:
- A GitHub account. I sign in to the CLI myself:
gh auth login --hostname github.com --git-protocol https --web(enter the one-time code at github.com/login/device). Then rungh auth setup-gitsogit pushuses that login. - A Cloudflare account (free).
- Access to my domain registrar’s dashboard, if I have a domain.
- A browser the agent can drive (for example the Claude in Chrome extension), with me logged in
to GitHub, Cloudflare, and the registrar.
- If more than one browser is connected, ask me which to use. Don’t pick one yourself.
- The extension may ask permission the first time it acts on a new site or subdomain, like a registrar’s account area. Tell me to approve it in the extension.
- If a browser action times out, a prompt or dialog is probably waiting in the browser or the extension’s side panel. Ask me to check before retrying.
- If the agent can’t drive a browser, give me exact click-by-click steps instead.
Phase 2: Documentation and the site brief
- List everything in my documentation folder. Read what the site needs, and skip anything I marked private.
- Check that the documentation answers each item below. For every gap, interview me with
short, specific questions, a few at a time. Don’t fill gaps with guesses.
- Purpose: what a visitor should understand and do.
- Audience: who they are, their problems, the questions they ask. Mark anything unconfirmed as an assumption.
- Offer: what is sold or provided. What must never be claimed.
- Pages: the route list and navigation, and what goes on each page.
- Voice: tone, words to use and avoid, heading style. Any approved copy to use verbatim.
- Design: colors, fonts, spacing, and a reference site or mood, even a rough one.
- Proof: real work, photos, numbers, and the source for each. Whether I have permission to publish each image or example.
- Contact: email, phone, address, and how visitors reach me. A static site cannot receive form submissions by itself, so choose a mailto link, a third-party form service, or a form embed. Never ship a form that sends nowhere.
- Legal: the business name as it should appear, any required notices, and whether a privacy page is needed (it usually is if a form collects personal details).
- If my documentation is thin, propose a small core set and write it with me: what the business is, who it serves, what it offers and must not claim, voice rules with banned words and good/bad examples, design tokens, and a short README saying which file wins when two disagree.
- Write
docs/site-brief.md: routes, a page-by-page content plan, design tokens, contact method, and pending items. Wait for my approval before building.
Phase 3: Repository and GitHub
- If the folder is not a git repository, run
git init. Confirm the branch ismain. - Create
.gitignorecoveringnode_modules/,dist/,.astro/,.env,.env.*, and OS or editor files. - Scan for secrets before the first commit: API keys, tokens, passwords,
.envfiles, private keys. Show me anything suspicious and keep it out of git. - With my approval, make a first commit of the documentation, create a private GitHub
repository (
gh repo create <name> --private --source . --push), and verify withgit ls-remote originand by opening the repo page.
Phase 4: Start the DNS move (skip if no domain, or if it’s registered at Cloudflare)
- Before touching anything, open the registrar’s DNS page and record every existing record in
the build log: websites, email (MX, SPF/TXT, DKIM, DMARC), verification TXT records, subdomains.
- Check whether email actually works today. MX records with no real mailbox or forwarding rule behind them are leftovers; real ones must survive the move.
- Registrar-provided email forwarding stops once nameservers leave the registrar. Plan its replacement in phase 10 before switching.
- Check DNSSEC. If it’s on at the registrar, turn it off there before changing nameservers.
Otherwise the domain can stop resolving. To verify, look for a DS record at the registry
(
nslookup -type=DS <domain> 1.1.1.1, ordig DS <domain>). It can be re-enabled through Cloudflare later. - In Cloudflare: Domains → Add a domain → Connect a domain. Do not choose “Transfer”: that moves the registration. Enter the domain, choose the Free plan, and let Cloudflare import the existing records.
- Compare the imported records to the list from step 1. Add anything missing. Keep real email records. Note which records only point at the old website or a parking page; they get deleted in phase 9.
- Copy the two nameservers Cloudflare assigns.
- Switch to the registrar, with my approval: set the nameservers to “Custom DNS” (the name varies by registrar), enter both Cloudflare nameservers, remove the old ones, and save.
- Back in Cloudflare, click “I updated my nameservers,” then “Check nameservers now.”
- Verify:
- The registry: find the TLD’s servers with
nslookup -type=NS com.(use the real TLD), then runnslookup -type=NS <domain> <tld-server>. - Public resolvers:
nslookup -type=NS <domain> 1.1.1.1and8.8.8.8. - The registry usually updates within seconds, but Cloudflare’s Active status can take hours. Continue with phases 5–8 meanwhile. If I’m recording, tell me to pause rather than wait on camera.
- The registry: find the TLD’s servers with
Phase 5: Create the Astro project
- Astro’s project creator expects an empty folder, and mine already has documentation. Create the
starter in a temporary folder outside the project:
npx -y create-astro@latest <temp-name> --template minimal --no-install --no-git --skip-houston --yes - Copy its files into my project without overwriting anything. Merge
.gitignoreby hand, skip the starter README, and rename thenameinpackage.json. - In
astro.config.mjs, setoutput: 'static'andsiteto the finalhttps://URL. - Run
npm install, thennpm run build. The build must pass before you write any site code. - Start the dev server in the background (
npx astro dev --background; manage it withnpx astro dev statusandnpx astro dev stop) and show mehttp://localhost:4321. - After adding or changing
src/content.config.ts, restart the dev server. Otherwise collections read as empty and pages render blank sections.
Phase 6: Build the site
- Design tokens first. Put every color, font, type size, spacing value, radius, and shadow in one CSS file (custom properties). Components use tokens only; nothing is hardcoded.
- Self-host fonts (download the
woff2files intopublic/fonts/) instead of loading them from a font service at runtime. - One site config file (for example
src/config/site.ts) for the name, contact details, navigation, and IDs, so each fact lives in exactly one place. - Content collections with schemas for anything repeated (services, projects, posts).
- A base layout carrying each page’s
<title>, meta description, canonical URL, Open Graph tags, favicon, and structured data (Organization or Person) where it fits. - Build every route in the navigation, plus a
404page, and a privacy page if a form collects personal details. Replace the starter favicon and remove starter content. - Images go through Astro’s image pipeline (
astro:assets) with one aspect ratio per image slot, and every image gets meaningful alt text. Use only images I have permission to publish. - Accessibility: one H1 per page, logical headings, visible focus styles, 44px tap targets, color contrast of at least 4.5:1 for body text, and a mobile menu that works without hover.
- Check each page at 375px and 1440px widths, with no sideways scrolling on a phone.
- Words: write from my documentation, never beyond it. For a large site, it works well to
build the pages first, then hand the copy to a second agent session with a written brief. That
session writes
docs/copy-review.md, listing every new sentence that needs my approval, the source for each claim, and its questions for me. - Mark everything unapproved or missing with a visible placeholder, and list it in the build log.
Phase 7: Pre-launch checks
npm run buildpasses with no errors or warnings you haven’t explained to me.- Search the source and the built
dist/forTBD,lorem,placeholder, starter text, and every banned word in my voice rules. Review each hit with me. - Click every navigation link, footer link, and button. No 404s except on purpose.
- Test the contact method end to end: send a real test message, or confirm the mailto opens.
- Show me each page and get my go-ahead to publish.
Phase 8: Deploy to Cloudflare from GitHub
-
Add
wrangler.jsoncat the repository root:{ "name": "<project-name>", "compatibility_date": "<today, YYYY-MM-DD>", "assets": { "directory": "./dist", "not_found_handling": "404-page" } }No Cloudflare adapter is needed for a static site, and Wrangler never needs installing or logging into on my computer. Validate after
npm run build:npx -y wrangler deploy --dry-run. -
Commit the site (explicit paths) and push to
main, with my approval. -
In Cloudflare: Workers & Pages → Create → Continue with GitHub. If GitHub asks to install or authorize the Cloudflare app, I approve it and choose which repositories it can see. Select the repository.
-
Confirm the settings: build command
npm run build, deploy commandnpx wrangler deploy, root path/. Then Deploy. -
Watch the build log to the end and open the
*.workers.devURL it prints. Check the home page and one inner page (inner pages may return 307 to a trailing-slash URL; that’s normal). -
Open the Worker’s Settings → Builds and confirm the production branch is
main. This first build is labeled “Manually deployed.” An “API token unavailable” notice may appear; phase 11 proves whether it matters.
Phase 9: Attach the domain, enforce HTTPS, redirect www
Wait until phase 4 shows the domain Active.
- With my approval, delete only the records that point the root domain or
wwwat the old website or a parking page (A, AAAA, or CNAME records for those names). Worker custom domains fail while those records exist. Never delete email or verification records. - Worker → Domains → Add domain: the root domain (subdomain empty), then
www. - SSL/TLS → Edge Certificates → Always Use HTTPS: on.
- Rules → Redirect Rules, from the template “Redirect from WWW to root”: 301, with Preserve
query string ticked. Cloudflare may warn that the rule “may not apply to your traffic” because
Worker custom domains don’t appear as DNS records. That’s expected; deploy anyway, and do not add
a DNS record for
www. - Verify with
curl -sI:https://<domain>/returns 200.http://<domain>/returns 301 tohttps://.https://www.<domain>/some/path?x=1returns 301 tohttps://<domain>/some/path?x=1.
- If the domain won’t load on my computer but public resolvers already answer correctly, my
computer cached the old answer. Test past the cache with
curl --resolve <host>:443:<cloudflare-ip>, and have me flush DNS (Windowsipconfig /flushdns, macOSsudo dscacheutil -flushcache; sudo killall -HUP mDNSResponder).
Phase 10 (optional): Email at the domain
Only if I want an address at this domain, or phase 4 found email that must keep working.
- Receiving: Cloudflare Email Routing forwards
name@<domain>to an inbox I already have. Add the destination address, confirm the verification email, and create the rule. Cloudflare adds its MX and SPF records. Don’t send test mail before routing exists; it’s lost. - Replying from the address (optional): receiving is free, but sending as the address needs an SMTP service (check its free tier and limits), plus the mail app’s “send mail as” setting. In Gmail, also set replies to come from the address the message was sent to.
- Authentication: SPF (one TXT record including every sender), DKIM for each sending service,
and DMARC starting at
p=none. Once real mail flows cleanly for a few weeks, consider tightening it. - Verify: send from an outside account and receive it; reply and confirm the headers show SPF,
DKIM, and DMARC
pass. Record in the build log where credentials live, never the credentials themselves, and which settings depend on them.
Phase 11: Prove the publish loop
- Make a small, real change (for example, update the build log), commit only that file, and push
to
mainwith my approval. - Show that Cloudflare started a build triggered by the push, not a manual deploy. Show the
GitHub commit’s check run succeeded (
gh api repos/<owner>/<repo>/commits/<sha>/check-runsshould listWorkers Builds: <project>with conclusionsuccess), and that the live site still returns 200. - Explain the everyday loop: edit →
npm run buildlocally → commit → push tomain→ live in about a minute. For changes I want to review before publishing, push to another branch: Workers Builds creates a preview version without touching production. - A failed Cloudflare build does not take the site down; the last good version stays live. Show me where build logs and past deployments are, and how to roll back.
Phase 12: Hand off
- Write
README.mdfor a non-developer: how to run the site locally, edit content, publish, and roll back. - Make sure
docs/build-log.mdlists every account used (names only, never passwords), every DNS change, the live URLs, open items, and every placeholder still on the site. - Close any browser tabs you opened, and stop the dev server if I don’t need it.
Definition of done
-
npm run buildpasses, and every navigation link works on the live site - No unapproved placeholders on public pages (or I have explicitly accepted each one)
- Private GitHub repo, production branch
main, and a clean working copy - Cloudflare Worker builds automatically on push to
main, proven by a real push - Domain Active on Cloudflare; root returns 200;
httpredirects tohttps;wwwredirects to the root - Email (if any) receives and replies, with SPF, DKIM, and DMARC passing
- Every page checked at 375px and 1440px widths
-
README.mdanddocs/build-log.mdare up to date
Traps people hit (check for these before they happen)
| Symptom | Cause and fix |
|---|---|
claude not found after installing it |
The install folder isn’t on PATH. Open a new terminal; if it’s still missing, add that one folder to PATH. |
| PATH broken after the agent edited it | An entry was appended without its ; separator, or the whole PATH was replaced. Fix the single entry; never rewrite the whole PATH. |
node, git, or gh “not recognized” right after installing |
The agent kept its old PATH. Restart the agent. |
Astro build fails on Windows with Cannot find native binding |
Microsoft Visual C++ Redistributable missing. Install it (phase 0). |
| Commits don’t show my GitHub avatar | The git email isn’t on my GitHub account. Fix user.email. |
git push asks for a password |
Run gh auth setup-git after gh auth login. |
| Agent asks which browser to use | Several browsers have the extension connected. Pick one; the agent must not guess. |
| Browser actions fail with “permission denied” or time out on a new site | The extension needs approval for that site, or a dialog is open. Approve it or dismiss the dialog. |
| Astro’s project creator refuses, or clutters the project folder | Folder not empty. Create the starter in a temp folder and copy it in (phase 5). |
| Pages show empty sections; log says a collection “does not exist or is empty” | Content config changed while the dev server was running. Restart it. |
| Cloudflare offers “Transfer a domain” | Wrong option. Use “Connect a domain”; transfer moves the registration. |
| Domain stops resolving after the nameserver change | DNSSEC was still on at the registrar. Turn it off there. |
| Email stopped after the nameserver change | Real MX, SPF, DKIM, or DMARC records weren’t in Cloudflare, or registrar forwarding ended. Restore the records and set up Email Routing. |
| “Add domain” on the Worker fails or complains about existing records | An old A/AAAA/CNAME record for that hostname still exists. Delete that record only. |
| Redirect rule warns it “may not apply to your traffic” | Expected with Worker custom domains. Deploy anyway; add no DNS record. |
| Site loads everywhere except my computer | My computer cached old DNS. Flush the local DNS cache. |
| Cloudflare Builds settings show an API token notice | Test with a real push. If the deploy step fails, choose or create a token in Settings → Builds. |
| A push didn’t trigger a build | The Cloudflare GitHub app can’t see the repo, or the push went to a branch other than production. Check the app’s repository access and Settings → Builds → Branch control. |
| The live site shows an old version | The latest build failed. Open the build log, fix locally, and push again. |
| Unfinished work went live | Something was committed while another person or agent was mid-edit. Commit explicit paths only, after git status. |