How exercises are hosted and how they render inside the Circle community.
Governance: this doc is the operational playbook. For the architectural contract (what exercises are, why they’re structured this way, what must stay locked) see BLI-ADR-001. Structural changes that alter the contract between hosting and architecture require an ADR amendment.
BigLeverVentures/bli-exercises (public)https://exercises.bigleverinstitute.org/Every exercise HTML file in the repo root is automatically published at https://exercises.bigleverinstitute.org/<filename>.html within about a minute of pushing to main.
The repo is the single source of truth for exercise content. Any draft or scratch copy that lives in a Claude Cowork workspace or elsewhere is not canonical and must not be edited after publishing — delete it or clearly mark it -DRAFT once the repo version is live. Two copies diverge silently; one copy cannot.
Every exercise ships as two paired files at the repo root:
exNN-<slug>.html — the standalone interactive exercise pageexNN-<slug>.oembed.json — an oEmbed response that tells embedding consumers (like Circle) how to render the page as a rich iframe<head> tags<!-- Canonical URL (authoritative source for this page) -->
<link rel="canonical" href="https://exercises.bigleverinstitute.org/exNN-<slug>.html">
<!-- Open Graph (for link previews in any consumer) -->
<meta property="og:type" content="website">
<meta property="og:title" content="Exercise NN — <Title>">
<meta property="og:description" content="<short description>">
<meta property="og:url" content="https://exercises.bigleverinstitute.org/exNN-<slug>.html">
<meta property="og:site_name" content="Big Lever Institute">
<!-- Twitter Card (fallback preview) -->
<meta name="twitter:card" content="summary_large_image">
<meta name="twitter:title" content="Exercise NN — <Title>">
<meta name="twitter:description" content="<short description>">
<!-- CRITICAL: oEmbed discovery pointer -->
<link rel="alternate"
type="application/json+oembed"
href="https://exercises.bigleverinstitute.org/exNN-<slug>.oembed.json"
title="Exercise NN — <Title>">
{
"version": "1.0",
"type": "rich",
"provider_name": "Big Lever Institute",
"provider_url": "https://exercises.bigleverinstitute.org/",
"title": "Exercise NN — <Title>",
"author_name": "Big Lever Institute",
"author_url": "https://exercises.bigleverinstitute.org/",
"width": 1080,
"height": <filled in by npm run measure>,
"html": "<iframe src=\"https://exercises.bigleverinstitute.org/exNN-<slug>.html\" width=\"100%\" height=\"<same height>\" frameborder=\"0\" allow=\"clipboard-write\" style=\"border:none;display:block;max-width:100%\" title=\"Exercise NN — <Title>\" loading=\"lazy\"></iframe>"
}
Every URL in both files MUST use exercises.bigleverinstitute.org — never bigleverventures.github.io. Iframely verifies the provider URL matches our claimed domain before approving rich embeds.
Manual height guessing is unreliable (we initially set Exercise 01 to 5200px; the real rendered height at 720px viewport is 7474px — a 30% clip). Every exercise’s height is measured by a headless browser and written into its companion JSON by:
npm run measure
This script (scripts/measure.mjs) walks every exNN-*.html at the repo root, launches Chromium at a 720px-wide viewport (narrower than our content’s 760px max-width, so it’s a conservative upper bound), waits for fonts and network idle, reads document.documentElement.scrollHeight, adds a 40px buffer, and patches both the top-level height field and the iframe’s height="..." attribute inside the JSON’s html string.
Run npm run measure any time you edit exercise copy, before committing. The diff on the .oembed.json files shows exactly which heights changed.
First-time setup on a new clone:
npm install
npx playwright install chromium
Three cache layers sit between a commit and what learners see. When you push a correction, walk this checklist:
Cache-Control: max-age=600. Self-heals within 10 minutes. Non-issue for anything but time-critical fixes.https://exercises.bigleverinstitute.org/ex01-resume-tailorer.html?v=2. Different URL → fresh fetch, no cached stale response.ex01-resume-tailorer.html and ex01-resume-tailorer.oembed.jsonexNN-<slug>.html and exNN-<slug>.oembed.jsontitle, html iframe src and title attribute — leave height as a placeholdernpm run measure — the script fills in the correct height in both placesmain/embed command — it renders as a full-height interactive iframedig +short exercises.bigleverinstitute.org returns bigleverventures.github.io. plus four GitHub Pages IPs (185.199.108-111.153)curl -sI https://exercises.bigleverinstitute.org/exNN-<slug>.html returns 200 OKhtml field/embed command in a test postCNAME exercises → bigleverventures.github.io, TTL 1 hour.CNAME file: contains one line, exercises.bigleverinstitute.org. Required for GitHub Pages custom domain.programs.bigleverinstitute.org is the Circle community (cohort courses). Exercises are the self-paced counterpart.