# Deploying the playground

The browser app in `browser/` builds its pages and assets statically and loads
a pre-compiled WASM module from `browser/public/`, so the deploy environment
does **not** need Emscripten. Cloudflare Pages also runs the dependency-free
`browser/functions/_middleware.ts` edge middleware on requests; it is deployed
alongside the static files and is not an Astro server-rendering adapter.

Target: **`posttrainllm.com`**, on Cloudflare Pages.

## ⚠ Deploy-gating prerequisite — the WASM artifacts must be in the repo at build time

`browser/public/tinygpt.js`, `tinygpt.wasm`, `tinygpt64.js`, and `tinygpt64.wasm`
are produced by Emscripten (`bash wasm/build_wasm.sh` + `bash wasm/build_wasm64.sh`)
and are listed in `.gitignore` by default. Cloudflare Pages does a fresh
`git clone` + `pnpm run build`; if these files aren't in the cloned tree, the
production site 404s on them and the playground fails to initialize.

Three resolutions, ranked:

1. **Commit the artifacts (recommended).** They're small (~80 KB each, ~230 KB
   total) and change rarely (only when the C++ kernels change). One-shot fix:

   ```sh
   git add -f browser/public/tinygpt.js browser/public/tinygpt.wasm \
              browser/public/tinygpt64.js browser/public/tinygpt64.wasm
   git commit -m "deploy: commit the compiled WASM artifacts"
   ```

   The `-f` overrides the `.gitignore` entries. Then on every C++ change,
   rebuild and commit the new artifacts as part of the same commit.

2. **Install Emscripten in Pages build environment.** CF Pages allows custom
   build commands; you'd `pnpm install && bash wasm/install_emsdk.sh && bash wasm/build_wasm.sh && bash wasm/build_wasm64.sh && pnpm run build`.
   Multiplies build time by ~3-5× and the emsdk install is finicky in CI.
   Not recommended unless option 1 becomes painful.

3. **Pre-built artifacts uploaded out-of-band** to R2 or similar, then a
   build-time fetch step. Most complex; only worth it if the WASM grows past
   1 MB and git becomes a real burden.

## Deploying — current state and how it actually works

**Heads-up.** The `posttrainllm` Pages project is currently set up as a
**direct-upload** project (Git Provider: No in `wrangler pages project list`).
That means every `git push` to GitHub does *nothing* for the live site —
deploys need an explicit `wrangler pages deploy`.

### Manual deploy (current path)

```bash
cd browser
pnpm run build
npx wrangler pages deploy dist --project-name=posttrainllm --commit-dirty=true
```

That uploads `dist/` to the production environment. Output ends with
`✨ Deployment complete! Take a peek over at https://<hash>.tinygpt.pages.dev`
and the canonical `posttrainllm.com` updates immediately (CF Pages
does no edge caching on `must-revalidate` files; HTML and assets are
re-fetched on every request).

### Auto-deploy via Git (recommended one-time setup)

If you want every `git push` to auto-deploy, connect Git provider in the
Cloudflare dashboard:

1. **Workers & Pages → posttrainllm → Settings → Builds & deployments → Connect to Git**
2. Select `PostTrainLLM/tinygpt`.
3. **Build configuration:**
   - Production branch: `main`
   - Framework preset: *None* (Astro auto-detected; Vite isn't in the preset list)
   - Root directory: `browser`
   - Build command: `pnpm install && pnpm run build`
   - Build output directory: `dist`
4. **Environment variables:** none required.
5. Save. Next push triggers an auto-deploy.

Cloudflare clones the whole repo, `cd`s into `browser/`, installs dependencies
and builds. First build is ~1 minute; subsequent builds are cached.

**Possible gotcha** with the auto-deploy: the docs library at `/docs/<slug>`
imports markdown from `../../../../docs/*.md`, which crosses the Astro `src/`
boundary. This is allowed by `vite.fs.allow: [".."]` in `astro.config.mjs` and
works locally and via direct upload — but if CF Pages' Vite/Node version
disagrees, the build will fail. If that happens, copy `docs/*.md` into
`browser/src/content/docs/` and update the `.astro` wrappers to import from
the new path.

## Custom domain

After the first successful deploy:

1. In the Pages project: **Custom domains → Set up a custom domain**.
2. Add `posttrainllm.com`. CF Pages will then sit in **Verifying**
   state and show you a CNAME to add.
3. **Add the CNAME manually**, even though `sarthakagrawal.dev` is on the
   same Cloudflare account. The "added automatically" claim that used to
   live in this section was wrong — auto-CNAME only happens when you set up
   the custom domain at the *same time* as the Pages project, via the
   create-app flow. Adding a custom domain post-deploy requires the DNS
   record manually:

   - **Zone**: `sarthakagrawal.dev` → DNS → Records → Add record
   - **Type**: `CNAME`
   - **Name**: `posttrainllm` (just the subdomain, not the full hostname)
   - **Target**: the value CF Pages shows (e.g. `posttrainllm.pages.dev`)
   - **Proxy**: orange-cloud Proxied
   - **TTL**: Auto

4. Back in Pages → Custom domains, click **Check DNS records**. It flips to
   **Active** in 30–60 s. SSL is then automatic.

After a minute or two: <https://posttrainllm.com>.

## What ships

The Vite build produces five HTML entry points (all listed in
`browser/vite.config.ts → build.rollupOptions.input`; any HTML file
not in that list is silently dropped from the production build):

- `/` — the playground (train, sample, swap backends).
- `/roadmap` — the performance journey, levers, and the speed-evolution
  chart. Each measured bar is reproducible from the bench button.
- `/devlog` — long-form notes from the AI-pairing session that produced
  the kernel work, including the negative results.
- `/speedup` — the historical speedup-curve chart (2.6× → 12.1× across Small → XL), explicitly unqualified until a paired hardware receipt reproduces it.
- `/webgpu-test` — the live kernel-parity diagnostic (30 GPU kernels
  checked against a reference, plus the GPU overfit gate). Useful as a
  "see it self-verify" link from the case study; keep it shipped.

The deployed assets total around 600 KB (WASM + JS chunks).

## After deploy — update the case study

The posttrainllm case study on the portfolio
(`sarthakagrawal927/portfolio: src/content/work/posttrainllm.mdx`) has `repo:` set
but `demo:` deliberately omitted until the URL is live. Add:

```yaml
demo: 'https://posttrainllm.com'
```

and the case-study header will render a `live demo ↗` link.

## Local sanity check before deploy

```sh
cd browser
pnpm install
pnpm run build      # produces browser/dist/
pnpm run preview    # serves dist/ on http://localhost:4173
```

If `pnpm run e2e` passes (which it does on `main`), the production build will
behave the same — the e2e drives the built bundle, not the dev server.
