From 0e3317115bae089a91e66e6575d977eb8f88a62e Mon Sep 17 00:00:00 2001 From: Sparsh Sam <110058692+sparshsam@users.noreply.github.com> Date: Fri, 3 Jul 2026 13:02:09 -0400 Subject: [PATCH] docs: update CLAUDE.md and AGENTS.md with daily batch architecture and deployment details Co-Authored-By: Claude --- AGENTS.md | 39 ++++++++++++++++++++++++++++++++++---- CLAUDE.md | 56 ++++++++++++++++++++++++++++++++++++++++++------------- 2 files changed, 78 insertions(+), 17 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index b8f57f1..aa6e914 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -2,10 +2,41 @@ ## Overview -WordWise is a reflective word-of-the-day practice app. Built with Node.js + Express. +WordWise is a reflective word-of-the-day practice app. Node.js + Express, deployed on Vercel behind Cloudflare. ## Rules -1. Dormant project. Do not make significant changes without explicit request. -2. Node.js/Express backend with serverless deployment. -3. Branch: \`feat/*\`, \`fix/*\`, \`docs/*\`, \`refactor/*\`, \`chore/*\`. +1. Active project (not dormant — daily batch system implemented July 2026). +2. Branching: `feat/*`, `fix/*`, `docs/*`, `refactor/*`, `chore/*`. +3. `main` branch is protected — requires PR with 1 approval. +4. Always test locally with `npm start` before deploying. + +## Architecture summary + +- **Daily batch**: 50 words selected via date-seeded PRNG (Mulberry32). Deterministic — same date always yields same pool. +- **Word serving**: `GET /api/word?slot=N` returns word at that slot position in today's pool. Client stores `nextSlot` in `localStorage`. +- **Media**: Pexels API fetches nature landscapes (photos 50%, videos 50%). Pre-fetched in background batch on first request of the day. +- **Cache**: `defCache` (word definitions, seeded from `words.json`), `mediaCache` (Pexels URLs). In-memory, per-instance. +- **Fallback**: Words not found in Dictionary API get a random curated word's definition from `words.json`. + +## Key technical details + +- Seeded PRNG: `seededRandom(dateString)` produces reproducible shuffle of the 20K+ word list, taking first 50. +- Slot wrapping: `(slot + 1) % 50` — after 49th word, wraps back to 0. +- Background pre-fetch: `preFetchBatchMedia()` iterates through all 50 words, fetching one Pexels URL each. Runs once per day per instance. +- Timeouts: Pexels fetch has 3s timeout, Dictionary API has 4s timeout. +- Curated fallback: `words.json` has 31 pre-defined words — enough to seed initial cache. For Dictionary misses, falls back gracefully with just a definition string (no mismatched phonetic). + +## Deployment + +```bash +vercel --prod --scope sparsh-sams-projects +``` + +Vercel project is linked — just run from repo root. Auto-deploys on push to `main`. + +## Domains & DNS + +- **App**: wordwise.kovina.org +- **DNS**: Cloudflare, CNAME proxied → wordwisehiccups.vercel.app +- **CF token**: `$CLOUDFLARE_API_TOKEN` in `.zshrc` (Zone:DNS:Edit scope) diff --git a/CLAUDE.md b/CLAUDE.md index 8e42119..7ca8fbf 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -2,31 +2,61 @@ ## Overview -Reflective word-of-the-day practice. Node.js + Express. +Reflective word-of-the-day practice. Node.js + Express. Production: https://wordwise.kovina.org (Vercel) ## Commands -npm start # Run server locally -vercel --prod # Deploy to production +```bash +npm start # Run server locally (port 3000) +vercel --prod # Deploy to Vercel production +git push origin main # Auto-deploys via Vercel Git integration +``` ## Architecture -- Daily batch system: 50 unique words per day, date-seeded deterministically -- Slot tracking: Each visitor progresses through the pool via localStorage -- Media pre-fetch: First request triggers async batch fetch of Pexels media -- Cache layers: In-memory defCache (Dictionary API) + mediaCache (Pexels) -- No external DB: Deterministic from date seed + in-memory caches +- **Daily batch system**: 50 unique words per day, date-seeded deterministically (Mulberry32 PRNG on `YYYY-MM-DD` seed) +- **Slot tracking**: Each visitor progresses through the pool via `localStorage` (`wordwise_slot` + `wordwise_day`) +- **Media pre-fetch**: First request of the day triggers async background fetch of 50 Pexels media URLs (50:50 video/photo) +- **Cache layers**: In-memory `defCache` (Dictionary API results) + `mediaCache` (Pexels URLs) — per serverless instance +- **No external DB**: Deterministic from date seed + in-memory caches ## Key files | File | Purpose | |------|---------| -| routes/word.js | Daily batch, word serving, media pre-fetch | -| views/index.html | Client with timezone bar, word display, media | -| words.json | Curated word pool (fallback definitions) | -| words.txt | 20K+ word pool for daily selection | +| `routes/word.js` | Daily batch generation, word serving, media pre-fetch | +| `views/index.html` | Client with timezone bar, word display, background media | +| `words.json` | 31 curated words with definitions (fallback for rare words) | +| `words.txt` | 20K+ English word pool for daily selection | + +## API + +### GET /api/word?slot=N + +Returns the Nth word from today's deterministic 50-word batch. + +Response: `{ word, phonetic, definition, example, partOfSpeech, audioUrl, background?, backgroundType?, photographer?, photoUrl?, nextSlot }` + +- `slot` defaults to 0 if omitted/invalid; wraps at 49 → nextSlot: 0 +- Media comes from pre-fetched Pexels cache or fetched on-demand ## Deployment -Custom domain on Vercel behind Cloudflare proxy. +- **Domain**: wordwise.kovina.org (Cloudflare proxied → Vercel) +- **Vercel project**: `wordwise` under `sparsh-sams-projects` team +- **Project ID**: `prj_ytl2Hm76xJYVrolTa8cnhsjBJAwa` +- **DNS**: CNAME `wordwise` → `wordwisehiccups.vercel.app` (proxied, Cloudflare) +- **Cloudflare zone**: kovina.org (NS: steven.ns.cloudflare.com / roxy.ns.cloudflare.com) +- Domain added via: `vercel domain add wordwise.kovina.org wordwise --scope sparsh-sams-projects` + +## Branch protection + +`main` requires 1 approving review, dismisses stale reviews, enforces for admins. No force pushes or deletions. + +## Environment variables + +| Variable | Source | Purpose | +|----------|--------|---------| +| `PEXELS_API_KEY` | Hardcoded fallback in code | Pexels API for background media | +| `CLOUDFLARE_API_TOKEN` | `~/.zshrc` | Cloudflare DNS management |