# Recipe — "a release page for your project" (the clerk pattern)

> A **golden path** for the release-page pattern: a static-assets site + a thin "clerk"
> Worker, cloned per project in ~an hour. **Copy the artifact, not the pipeline** — the clerk's
> logic is product-agnostic; only names, colors, and content change. The origin instance is
> `services/ephemera-releases/` (this site); the pattern has been cloned live multiple times, and
> a heavier variant with D1/R2 version manifests exists for products that ship versioned binaries —
> reach for that shape only if you need downloads, not docs.

## What you end up with

- `https://<product>.acme.com` — landing page + the product's canonical markdown served raw
  to agents (content-negotiation: `Accept: text/markdown` on `.md` paths returns text/markdown
  with CORS) and rendered for humans.
- **Agent surface:** `llms.txt`, `.well-known/<product>.json` (name/tagline/version/paths
  manifest), `install.sh` (one curl → working toolchain).
- A **behavioral self-test collection** (`<product>-releases.api.md`) proving the
  live site: `GET /` → 200 · `GET /<CONTRACT>.md` → 200 · `POST /feedback` → `{"ok":true}`.

## What the clerk Worker is FOR (and not)

The Worker exists for exactly three things: markdown content-negotiation (+CORS) on
`.md`/`llms.txt`/`install.sh`; `POST /feedback`; and neutral-host asset fetch (the same-zone
**error 1042** fix — a Worker fetching its own zone's URL loops; fetch assets via the binding).
Everything else is static assets. Resist adding logic to it.

**Feedback is a mailto, not a database.** The landing page's feedback affordance is a mailto link
(inbox, not table — nobody reads a D1 of feedback). The clerk's `/feedback` endpoint stays as
dormant plumbing for logic parity across clones; deliberately **no d1_databases binding**
(decision 2026-07-01).

## The clone ritual

1. `cp -R services/ephemera-releases services/<product>-releases` **in your project** (copy the
   ARTIFACT — do not re-derive it from a spec).
2. `wrangler.jsonc`: `name`, the `routes` custom_domain (`<product>.acme.com`), keep
   `run_worker_first: true` + the `ASSETS` binding + `workers_dev: true`.
3. `src/index.js`: product names/comments only — the logic is product-agnostic by design.
4. `public/`: re-skin `index.html` (wordmark, tagline, palette, OG image), fresh `favicon.svg`,
   rewrite `llms.txt` + `.well-known/<product>.json` + `install.sh` for your toolchain's files.
5. `sync.sh`: copy it verbatim, then edit ONLY its `SITE-LOCAL CONFIG` block — `TOP_DOCS` (your
   canonical root docs), `SURFACE_PATHS` (the files that must mention every document), `ROOT_STAGED`.
   Everything else is product-agnostic. The site serves your markdown as the product, so `public/`
   is a MIRROR: never hand-copy into it, always sync. `sync.sh` runs the whole chain — lint →
   sanitize → four-surface parity → stage → drift/orphan re-diff — and stages nothing if any gate
   is red. `./sync.sh --check` is the read-only form for a verification entrypoint.
6. Author `<product>-releases.api.md` (3 endpoints above) and fire it vs `wrangler dev` before
   deploying.
7. Deploy (human gate — it's outward-facing): `./sync.sh && npx wrangler deploy`. Then fire the
   collection `--env prod` and write the Notebook lines.

## Gotchas (all hit in the wild)

- **Error 1042**: the clerk must fetch assets through `env.ASSETS`, never its own public URL.
- **Custom domain**: `routes: [{ pattern, custom_domain: true }]` — no DNS record to create;
  Cloudflare materializes it. The `workers.dev` URL stays live alongside (find the account
  subdomain via `GET /accounts/<id>/workers/subdomain` if you need it).
- **Cold start**: a transient 500 on the content-negotiation path in the first seconds after
  deploy is propagation, not a bug — re-probe before debugging.
- **`wrangler deployments list`** shows an "Upload" version plus a separate "deployment" entry —
  the pair is one deploy, not two.
- Keep the outer README fence 4 backticks if your markdown embeds ```-fenced examples (GitHub
  rendering breaks otherwise; bit a sibling product's flagship README 2026-07-01).
- **Stage every served subtree, not just the obvious one.** Anything under `public/` that also
  exists in canon needs a `sync.sh` line, or it silently fossilizes at whatever it was the day it
  was hand-copied. The origin instance served a 5-week-stale test script — pre-genericization, with
  a real owner name the sanitizer had since scrubbed — because the staging list skipped
  `public/scripts/` (found 2026-07-30 by the drift check, which is why that check exists).

## Registry

Every instance carries a `releases-page-clerk` (or equivalent) entry in its project
`MANIFEST.toml` — keep `maturity` honest and point `pattern_doc` here.
