Document: loomworks-example-generator-implementation-notes-v0_1
Status: complete — built, deployed, live
Date: 2026-07-16
By: Claude Code (CC) on DUNIN7-M4
Implements: loomworks-example-generator-cr-v0_2 (D1–D8), record-corrected by loomworks-example-generator-cr-v0_3
Repo: loomworks-marketing, branch main (feature branches feat-example-generator + feat-example-generator-d8, merged via PR #1)
> Static page + three same-origin Cloudflare Pages Functions on the marketing site. Two-step analyze→illustrate chain (D1–D7) plus a third, separate Foundation Document draft call (D8). Built across two feature branches merged to main on 2026-06-11; the record did not reflect this until this note and CR v0.3. Model upgraded from claude-haiku-4-5 to claude-sonnet-4-6 on 2026-07-16 (commit 119232e) — this note also documents that change, since it is the immediate reason the CR is being corrected now.
| | |
|---|---|
| Deployed | https://loomworks.doneinseven.com/example-generator/ (Cloudflare Pages) |
| Endpoints | POST /api/analyze, POST /api/illustrate, POST /api/foundation-draft |
| Merge | PR #1, feat-example-generator + feat-example-generator-d8 → main, commit 99b2eb6 (2026-06-11) |
| Model (current) | claude-sonnet-4-6, as of main 119232e (2026-07-16) |
| Model (history) | shipped Sonnet 4.6 (78f9cda) → switched to Haiku 4.5 for launch cost control (f2dc67f, 2026-06-08) → switched back to Sonnet 4.6 (119232e, 2026-07-16) |
| Rate limits (current) | 20 req/hour/IP, 100 req/day global — confirmed at main 119232e |
| Rate limits (history) | PER_IP_HOURLY = 20 since the first commit (78f9cda), never changed. GLOBAL_DAILY shipped at 1000, changed once to 100 in f2dc67f (the Haiku switch), unchanged since. |
| Rate-limit storage | KV namespace bound as RATE_LIMIT_KV — bound on the Cloudflare Pages dashboard only; not declared in any file in the repo |
| Key custody | env.ANTHROPIC_API_KEY, Worker/Pages Function secret; never in client-served files |
| Persistence | none — no engine call, no engagement, no storage anywhere in the chain, including D8 |
| Commit | What |
|---|---|
| 78f9cda | D1–D7: page + same-origin Worker (CR v0.1). Shipped with MODEL = claude-sonnet-4-6, PER_IP_HOURLY = 20, GLOBAL_DAILY = 1000. |
| f2dc67f | Switch to Haiku 4.5, lower GLOBAL_DAILY to 100 (Operator-confirmed launch settings; ~$2–3/day worst case). |
| 1174d2f | Trim the API key on read — defends against a 401 from a bad paste into the secret. |
| cb07f54 | Strip markdown code fences + doc scaffolding from model HTML output (sanitizeFragment). |
| 5731886, 1a93873, df21f35 | D4 output-rendering polish — echo the prospect's own D1 inputs verbatim above the model output; field-label wording. |
| ac4f1f5 | D8: Foundation Document draft (third call) + a full styling port over the whole page. |
| e51aeb8 | Two chips added beyond the CR's seven: Consulting project, Marketing support. |
| 99b2eb6 | PR #1 merged — feat-example-generator + feat-example-generator-d8 → main. |
| 3328a61 (branch fix-rate-limit-binding, not merged) | wrangler.toml-based KV binding, added to debug a runtime-injection failure (BISECTION commit). Abandoned; main still has no wrangler.toml. |
| e94aaef | Empty commit, "pick up RATE_LIMIT_KV production binding" — a rebuild trigger, not a code change; the working fix was the dashboard binding, not the file-based one. |
| 10e1c4c / 119232e | MODEL → claude-sonnet-4-6, merged to main (2026-07-16). |
VOCAB constant in _lib.js covers all five required elements: the four rooms in order, the lifecycle vocabulary, the authority posture, the plain-terms discipline, and "what recall is." Shared verbatim across all three system prompts (analyze, illustrate, foundation-draft).innerHTML into their own regions; copy, download, and print affordances all present and working (composedHtml() assembles the retained artifact). A fourth affordance — Start over (reset) — exists beyond the CR's three; minor, not flagged as a deviation.composeFoundationDraft), D8.3 (read-only textarea + Copy, no persistence), D8.4 (the qualifier "a draft toward the engagement's Foundation Document" is server-composed, never model-generated, and the word "seed" never appears in any user-facing string), D8.5 (branch state — feat-example-generator existed pre-D8 at 78f9cda; D8 was built as the delta on top of it, per the CR's first branch).
1. Nine chips shipped, not seven. Commit e51aeb8 added "Consulting project" and "Marketing support" chips after the CR's seven. Same pattern (work/audience/done/constraints seed), same UI treatment. Not documented in any CR version.
2. Cloudflare Pages Functions, not a separately-bound Cloudflare Worker. D5 describes "a Cloudflare Worker bound to the Pages project" and a secret set via wrangler secret. What's actually built is native Cloudflare Pages Functions — the functions/api/*.js file-based routing convention, colocated with the static site, not a separate Workers service bound to the Pages project. The practical effect the CR cares about (same-origin, no CORS, key server-side only) holds either way, and the secret is in fact set as a Pages secret (env.ANTHROPIC_API_KEY, confirmed via the dashboard per .env.example's own instructions) rather than via wrangler secret in the literal sense. This is a terminology/mechanism gap between the CR text and what CC actually built, not a functional gap.
3. Rate-limit numbers differ from the CR's proposed defaults. The CR proposed "10/hour/IP" as a starting number, to be confirmed in the handoff. The number that shipped — from the very first commit — was 20/hour/IP, double the CR's proposal. The global daily cap started at 1000 (not proposed by the CR at all) and was lowered to 100 alongside the Haiku switch. No handoff document recording this confirmation was found in the record; this note is the first record-side confirmation.
4. sanitizeFragment — a code-side guarantee the CR only asked for on D8. D8.2 explicitly calls for "guarantee-in-code, not prompt-trust" for the draft call. The same discipline was independently applied to the D7 chain's HTML output (sanitizeFragment strips code fences and document scaffolding server-side, added in cb07f54, after the initial build) — the CR's D4 only asks that the model be instructed not to add scaffolding; it doesn't ask for a server-side strip. This is a strictly stronger guarantee than specified, not a gap.
5. API-key trim-on-read. callAnthropicRaw trims env.ANTHROPIC_API_KEY before use (1174d2f) — defensive against a stray newline from a bad secret paste causing a 401. Not specified anywhere in the CR; a pure hardening addition.
No wrangler.toml in loomworks-marketing. Confirmed on main — the file does not exist. The RATE_LIMIT_KV binding, and any other Pages binding, is configured only on the Cloudflare Pages dashboard, outside version control. .env.example documents this explicitly as the intended pattern (secrets and bindings are dashboard/CLI-configured, never committed). One unmerged branch, fix-rate-limit-binding, added a wrangler.toml on 2026-06-11 while debugging a KV runtime-injection failure (see the BISECTION commit 3328a61) — it was not adopted; the fix that actually shipped was a dashboard-side rebind plus an empty "pick up the binding" rebuild commit (e94aaef) on main. The unmerged branch still exists in the repo, diverged from main at e94aaef.
Rate limiting fails open when RATE_LIMIT_KV is unbound. rateLimit() in _lib.js checks for the binding first; if absent, it returns { ok: true } immediately (no cap enforced) and logs a server-side warning (RATE_LIMIT_KV is not bound — rate limiting is DISABLED. Bind it before go-live.). Confirmed directly: running the deployed code locally via wrangler pages dev without a KV binding, every /api/* request logged that exact warning and succeeded uncapped. This is a deliberate choice (so preview deploys and local dev work without provisioning KV) but means a future misconfiguration that drops the dashboard binding — a Pages project setting change, not a code change — silently disables rate limiting rather than failing the request. Nothing in the CR specifies fail-open vs. fail-closed; this is the code's own choice, undocumented anywhere else in the record until now.
npm run build + npx wrangler pages dev dist --compatibility-date=2026-06-30 (the installed wrangler's runtime caps at 2026-06-30; the default today-dated compat date fails to start). .dev.vars (gitignored) supplies ANTHROPIC_API_KEY locally; no local KV binding — the fail-open warning above was observed on every request in this mode. All three endpoints (analyze, illustrate, foundation-draft) exercised end to end and returned 200 with well-formed output, once on claude-sonnet-4-6 and once on claude-haiku-4-5 (identical four-field input both times).119232e merge and push, and after the Cloudflare Pages build completed, GET /example-generator/ and POST /api/analyze were both exercised directly against https://loomworks.doneinseven.com. Page rendered; the analyze call returned all five required <h3> sections in the CR's exact order.fix-rate-limit-binding branch — unmerged, diverged from main at e94aaef. Contains the wrangler.toml KV-binding experiment. Not cleaned up; not referenced by any CR or handoff found in the record.
DUNIN7 — Done In Seven LLC — Miami, Florida
Loomworks Example Generator — implementation notes — v0.1 — 2026-07-16
loomworks-marketing main 119232e; deployed at loomworks.doneinseven.com/example-generator/; model claude-sonnet-4-6.