From 73d42030aebd7520ba285ab8d7605cd99dd458e7 Mon Sep 17 00:00:00 2001 From: TMHSDigital <154358121+TMHSDigital@users.noreply.github.com> Date: Tue, 22 Sep 2026 19:43:03 -0400 Subject: [PATCH 1/2] feat(site): deploy to Pages behind the parity and site checks, with card meta and a 404 Co-Authored-By: Claude Opus 5.5 (1M context) --- .github/workflows/site.yml | 78 +++++++++++--- CHANGELOG.md | 8 +- README.md | 3 + scripts/build_site.py | 104 ++++++++++++++----- scripts/check_site_links.mjs | 194 +++++++++++++++++++++++++++++++++++ scripts/og_image.html | 57 ++++++++++ site/index.html | 15 +++ site/og.png | Bin 0 -> 45665 bytes 8 files changed, 423 insertions(+), 36 deletions(-) create mode 100644 scripts/check_site_links.mjs create mode 100644 scripts/og_image.html create mode 100644 site/og.png diff --git a/.github/workflows/site.yml b/.github/workflows/site.yml index 1836e98..d6de84e 100644 --- a/.github/workflows/site.yml +++ b/.github/workflows/site.yml @@ -3,9 +3,10 @@ name: Site # The site at tmhsdigital.github.io/plumbline carries a JavaScript # reimplementation of the calibration floor. If it disagrees with the Python, # the site misreports the tool, which is worse than having no site. This -# workflow holds the two together. The site is assembled inside this job, and -# the deploy job (not yet added) will need it, so a wrong calculator or a -# broken doc never ships. A stale site is the better failure. +# workflow holds the two together, checks the assembled site as a visitor would +# meet it, and only then deploys. Pull requests run both checks and never +# deploy; a push to main or a manual run deploys only if both pass. A stale +# site is the better failure. # # It is deliberately separate from ci.yml and release.yml: it shares no jobs, # no concurrency group, and no permissions with either. @@ -18,9 +19,11 @@ on: permissions: contents: read +# A newer push to a pull request makes its older run pointless. On main, a run +# is never cancelled part way, so a deploy is never cut off. concurrency: group: site-${{ github.ref }} - cancel-in-progress: true + cancel-in-progress: ${{ github.event_name == 'pull_request' }} jobs: parity: @@ -44,15 +47,11 @@ jobs: - name: The committed fixture is what the Python produces today run: uv run python scripts/floor_golden.py --check - # The renderer's refusals (raw HTML off the allowlist, broken links and - # anchors, remote images, a modified vendored file) each have a case. - - name: The doc renderer refuses what it should - run: node scripts/render_docs.mjs --self-test - # Regenerates the worked example from the mock adapter, by the command - # docs/example-report.md records, and refuses if the report is stale. - # Renders the repository's docs from this commit, and refuses on a broken - # link or anchor, or raw HTML outside the allowlist. + # docs/example-report.md records, and refuses if any line of the report + # differs from what that command prints today. Renders the repository's + # docs from this commit, and refuses on a broken link or anchor, or raw + # HTML outside the allowlist. - name: Assemble the site run: uv run python scripts/build_site.py --out _site @@ -63,3 +62,58 @@ jobs: run: | node --version node scripts/check_floor_parity.mjs _site/example-run.json + + # The exact bytes the deploy job publishes. On a pull request this is + # also a downloadable preview of the site. + - name: Keep the assembled site + uses: actions/upload-pages-artifact@v5 + with: + path: _site + + docs: + name: the assembled site's links, anchors, and meta tags resolve + needs: parity + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v5 + + # The renderer's refusals (raw HTML off the allowlist, broken links and + # anchors, remote images, a modified vendored file) each have a case. + - name: The doc renderer refuses what it should + run: node scripts/render_docs.mjs --self-test + + - name: Fetch the assembled site + uses: actions/download-artifact@v8 + with: + name: github-pages + path: artifact + + # Checked as published, not as built: the same archive the deploy job + # serves, unpacked. + - name: Every link, anchor, and meta tag resolves, and nothing loads from another origin + run: | + mkdir site-as-published + tar -xf artifact/artifact.tar -C site-as-published + node scripts/check_site_links.mjs site-as-published + + deploy: + name: deploy to GitHub Pages + needs: [parity, docs] + if: github.ref == 'refs/heads/main' && (github.event_name == 'push' || github.event_name == 'workflow_dispatch') + runs-on: ubuntu-latest + permissions: + pages: write + id-token: write + environment: + name: github-pages + url: ${{ steps.deployment.outputs.page_url }} + # One deploy at a time; a queued one waits rather than cancelling. + concurrency: + group: pages + cancel-in-progress: false + + steps: + - name: Publish the checked site + id: deployment + uses: actions/deploy-pages@v5 diff --git a/CHANGELOG.md b/CHANGELOG.md index 07109e0..3092521 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,7 +17,6 @@ different event from one that moved because it was wrong. `synthetic_floor` that reproduces numpy's seeded random stream draw for draw. `scripts/floor_golden.py` exports golden values from the Python and `scripts/check_floor_parity.mjs` holds the port to them within 1e-9 in CI. - The page is not deployed yet. - The site page: the argument, a worked example that derives the example report's ECE line from its 105 rows in the browser, and a sample-size planner. `scripts/build_site.py` regenerates the example from the mock adapter at build @@ -29,6 +28,13 @@ different event from one that moved because it was wrong. the runner's Node. Each page names the commit and build time it came from. The build fails on a broken link or anchor, on raw HTML outside a short allowlist, and on a modified vendored renderer. +- The site is live at , deployed by + `site.yml` from `main` only after the parity check passes and + `scripts/check_site_links.mjs` finds every link, anchor, and meta tag in the + assembled site resolving and nothing loading from another origin. Every page + carries a canonical URL and Open Graph and Twitter card tags; the card image + (`site/og.png`) is rendered from `scripts/og_image.html`. Missing paths get a + 404 page that links back. ### Changed diff --git a/README.md b/README.md index 58cc66d..0cbdc3a 100644 --- a/README.md +++ b/README.md @@ -5,6 +5,9 @@ Measure whether a decision model's probabilities are trustworthy on your own labeled data, and decide what to do about it. +Check where your own ECE sits against its floor, in the browser, with nothing +installed: **[tmhsdigital.github.io/plumbline](https://tmhsdigital.github.io/plumbline/)**. + > **v0.1.0, one maintainer.** The measurement behaviour is settled; the Python > API and the CLI flags are not, and will change in v0.2. Pin a version if you > build on it. diff --git a/scripts/build_site.py b/scripts/build_site.py index a1123e5..ba2564f 100644 --- a/scripts/build_site.py +++ b/scripts/build_site.py @@ -351,11 +351,12 @@ def render_docs(prov: Provenance) -> dict[str, dict[str, str]]: return rendered -def _nav(current: str | None, up: str) -> str: +def _nav(current: str | None, up: str, docs: str = "") -> str: + """Links to the explainer (``up``) and every doc (``docs`` + slug).""" items = [f'
  • The ECE floor
  • '] for doc in DOCS: mark = ' aria-current="page"' if doc.slug == current else "" - items.append(f'
  • {html.escape(doc.label)}
  • ') + items.append(f'
  • {html.escape(doc.label)}
  • ') joined = "\n ".join(items) return f'' @@ -367,6 +368,47 @@ def _nav(current: str | None, up: str) -> str: "%3Cpath d='M5 10h6l-3 5z' fill='%23555'/%3E%3C/svg%3E" ) +#: Where Pages serves the site. Canonical and Open Graph URLs are absolute, and +#: the 404 page links by absolute path, because it is served at any depth. +SITE_URL = "https://tmhsdigital.github.io/plumbline/" +SITE_PATH = "/plumbline/" +OG_IMAGE_ALT = ( + "A calibration claim has a floor: ECE 0.074 on 105 rows sits below the floor's " + "95th percentile of 0.111, so the result is inconclusive." +) + + +def social_meta(title: str, description: str, url: str) -> str: + """Canonical, Open Graph, and Twitter card tags for one page. + + ``site/index.html`` carries the same tags written out by hand, and + ``scripts/check_site_links.mjs`` checks every page has them and that its + canonical and og:url name the page itself. + """ + title, description = html.escape(title), html.escape(description) + image = f"{SITE_URL}og.png" + alt = html.escape(OG_IMAGE_ALT) + return "\n".join( + ( + f'', + '', + '', + f'', + f'', + f'', + f'', + '', + '', + f'', + '', + f'', + f'', + f'', + f'', + ) + ) + + PAGE = """ @@ -374,15 +416,16 @@ def _nav(current: str | None, up: str) -> str: {title} | plumbline +{meta} - - + +
    -

    plumbline

    +

    plumbline

    {nav}
    @@ -398,6 +441,19 @@ def _nav(current: str | None, up: str) -> str: """ +def _page(title: str, description: str, meta: str, root: str, nav: str, body: str) -> str: + return PAGE.format( + title=html.escape(title), + description=html.escape(description, quote=True), + meta=meta, + icon=ICON, + root=root, + nav=nav, + body=body, + repo=REPO, + ) + + def write_docs(out: Path, prov: Provenance, rendered: dict[str, dict[str, str]]) -> None: docs = out / "docs" docs.mkdir() @@ -407,14 +463,8 @@ def write_docs(out: Path, prov: Provenance, rendered: dict[str, dict[str, str]]) f'

    {prov.line(doc)}

    \n' f'
    \n{rendered[doc.slug]["html"]}
    ' ) - page = PAGE.format( - title=html.escape(doc.label), - description=html.escape(doc.blurb, quote=True), - nav=_nav(doc.slug, "../"), - body=body, - repo=REPO, - icon=ICON, - ) + meta = social_meta(f"{doc.label} | plumbline", doc.blurb, f"{SITE_URL}docs/{doc.slug}.html") + page = _page(doc.label, doc.blurb, meta, "../", _nav(doc.slug, "../"), body) (docs / f"{doc.slug}.html").write_text(page, encoding="utf-8") listing = "\n".join( @@ -431,17 +481,24 @@ def write_docs(out: Path, prov: Provenance, rendered: dict[str, dict[str, str]]) f"at {at}.

    \n" f'
      \n{listing}\n
    \n' ) - (docs / "index.html").write_text( - PAGE.format( - title="Documentation", - description="plumbline's documentation, rendered from the repository.", - nav=_nav(None, "../"), - body=index, - repo=REPO, - icon=ICON, - ), - encoding="utf-8", + description = "plumbline's documentation, rendered from the repository." + meta = social_meta("Documentation | plumbline", description, f"{SITE_URL}docs/") + page = _page("Documentation", description, meta, "../", _nav(None, "../"), index) + (docs / "index.html").write_text(page, encoding="utf-8") + + +def write_404(out: Path) -> None: + """Pages serves this for any missing path, at any depth, so it links absolutely.""" + body = ( + '' ) + nav = _nav(None, SITE_PATH, f"{SITE_PATH}docs/") + meta = '' + page = _page("Not found", "No page at this address.", meta, SITE_PATH, nav, body) + (out / "404.html").write_text(page, encoding="utf-8") def main() -> int: @@ -470,6 +527,7 @@ def main() -> int: shutil.copytree(SITE, out, ignore=shutil.ignore_patterns("vendor")) (out / "example-run.json").write_text(json.dumps(example, indent=1) + "\n", encoding="utf-8") write_docs(out, prov, rendered) + write_404(out) print(f"site assembled in {out}") return 0 diff --git a/scripts/check_site_links.mjs b/scripts/check_site_links.mjs new file mode 100644 index 0000000..711003c --- /dev/null +++ b/scripts/check_site_links.mjs @@ -0,0 +1,194 @@ +// Check the assembled site as a visitor would meet it. Runs on the runner's own +// Node with the standard library only. +// +// node scripts/check_site_links.mjs _site +// node scripts/check_site_links.mjs https://tmhsdigital.github.io/plumbline/ +// +// Given a directory it checks the build before deploy; given the site's URL it +// checks what is actually being served. Either way, starting from the explainer +// and following every internal link, it fails if: +// +// - an internal link, stylesheet, script, or image does not resolve, +// - a #fragment names no id on the page it points to, +// - a page loads anything (script, stylesheet, image, frame) from another origin, +// - a page lacks its canonical, Open Graph, or Twitter card tags, or its +// canonical and og:url do not name the page itself, +// - og.png is not a 1200x630 PNG, +// - the 404 page is missing, links anywhere that does not resolve, or (live) +// a missing path is not answered with it and a 404 status. + +import { readFile } from "node:fs/promises"; +import path from "node:path"; + +const SITE_URL = "https://tmhsdigital.github.io/plumbline/"; +const SITE_PATH = new URL(SITE_URL).pathname; // "/plumbline/" + +const target = process.argv[2]; +if (!target) { + console.error("usage: check_site_links.mjs "); + process.exit(2); +} +const live = /^https?:\/\//.test(target); +const base = live ? (target.endsWith("/") ? target : `${target}/`) : null; + +// Fetch one site path ("" is the explainer). Returns { status, text, bytes }. +async function get(sitePath) { + if (live) { + const response = await fetch(base + sitePath, { redirect: "follow", cache: "no-store" }); + const bytes = new Uint8Array(await response.arrayBuffer()); + return { status: response.status, bytes, text: new TextDecoder().decode(bytes) }; + } + try { + const bytes = new Uint8Array(await readFile(path.join(target, ...sitePath.split("/")))); + return { status: 200, bytes, text: new TextDecoder().decode(bytes) }; + } catch { + return { status: 404, bytes: new Uint8Array(), text: "" }; + } +} + +// Map a reference found on the page at `from` to a site path, or report it as +// external. `from` is a site path such as "docs/plan.html". +function resolve(reference, from) { + if (/^(data|mailto):/i.test(reference)) return { skip: true }; + let url; + if (/^https?:\/\//i.test(reference)) { + if (!reference.startsWith(SITE_URL)) return { external: reference }; + url = new URL(reference); + } else if (reference.startsWith("//")) { + return { external: reference }; + } else { + url = new URL(reference, SITE_URL + from); + } + if (!url.pathname.startsWith(SITE_PATH)) return { error: `${reference} leaves the site's path` }; + let sitePath = decodeURIComponent(url.pathname.slice(SITE_PATH.length)); + if (sitePath === "" || sitePath.endsWith("/")) sitePath += "index.html"; + return { sitePath, fragment: url.hash ? decodeURIComponent(url.hash.slice(1)) : null }; +} + +// Every tag with a reference, as [tag, attribute, value, rel]. +function references(html) { + const found = []; + for (const [, tag, attributes] of html.matchAll(/<(a|link|script|img|iframe|source|video|audio)\b([^>]*)>/gi)) { + const rel = /\brel="([^"]*)"/i.exec(attributes)?.[1] ?? ""; + for (const [, name, value] of attributes.matchAll(/\b(href|src)="([^"]*)"/gi)) { + found.push([tag.toLowerCase(), name.toLowerCase(), value.replaceAll("&", "&"), rel.toLowerCase()]); + } + } + return found; +} + +const ids = (html) => new Set([...html.matchAll(/\bid="([^"]+)"/g)].map((match) => match[1])); +const meta = (html, key, value) => + new RegExp(``).test(html); +const metaValue = (html, key) => + new RegExp(``).exec(html)?.[1] ?? null; + +const problems = []; +const pages = new Map(); // site path -> html, for fragment checks +const checked = new Set(); + +// The canonical URL a page should declare. +const canonicalFor = (sitePath) => + SITE_URL + (sitePath.endsWith("index.html") ? sitePath.slice(0, -"index.html".length) : sitePath); + +async function page(sitePath) { + if (!pages.has(sitePath)) { + const { status, text } = await get(sitePath); + pages.set(sitePath, status === 200 ? text : null); + } + return pages.get(sitePath); +} + +function checkSocial(sitePath, html) { + const want = canonicalFor(sitePath); + const canonical = //.exec(html)?.[1]; + if (canonical !== want) problems.push(`${sitePath}: canonical is ${canonical ?? "missing"}, expected ${want}`); + if (metaValue(html, "og:url") !== want) problems.push(`${sitePath}: og:url does not name the page`); + for (const key of ["og:title", "og:description", "og:image:alt", "twitter:title", "twitter:description"]) { + if (!metaValue(html, key)) problems.push(`${sitePath}: ${key} is missing or empty`); + } + for (const [key, value] of [ + ["og:image", `${SITE_URL}og.png`], + ["og:image:width", "1200"], + ["og:image:height", "630"], + ["twitter:card", "summary_large_image"], + ["twitter:image", `${SITE_URL}og.png`], + ]) { + if (!meta(html, key, value)) problems.push(`${sitePath}: ${key} is not ${value}`); + } +} + +// Check one page's references; queue the internal HTML pages it links to. +async function crawl(sitePath, { social = true, servedAs = sitePath } = {}) { + if (checked.has(sitePath)) return; + checked.add(sitePath); + const html = await page(sitePath); + if (html === null) { + problems.push(`${sitePath}: not found`); + return; + } + if (social) checkSocial(sitePath, html); + const queue = []; + for (const [tag, , value, rel] of references(html)) { + const where = `${servedAs}: <${tag}> ${value}`; + if (value.startsWith("#")) { + if (!ids(html).has(decodeURIComponent(value.slice(1)))) problems.push(`${where}: no such id on this page`); + continue; + } + const loads = tag !== "a" && !(tag === "link" && /\bcanonical\b/.test(rel)); + const resolved = resolve(value, servedAs); + if (resolved.skip) continue; + if (resolved.error) { + problems.push(`${where}: ${resolved.error}`); + continue; + } + if (resolved.external) { + if (loads) problems.push(`${where}: loads from another origin`); + continue; + } + const { sitePath: to, fragment } = resolved; + if (to.endsWith(".html")) { + const linked = await page(to); + if (linked === null) problems.push(`${where}: does not resolve`); + else { + if (fragment && !ids(linked).has(fragment)) problems.push(`${where}: no id "${fragment}" on ${to}`); + queue.push(to); + } + } else { + const { status } = await get(to); + if (status !== 200) problems.push(`${where}: does not resolve (${status})`); + } + } + for (const next of queue) await crawl(next); +} + +await crawl("index.html"); + +// og.png: a real PNG of the size the tags claim. +{ + const { status, bytes } = await get("og.png"); + const view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength); + const png = status === 200 && bytes.length > 24 && view.getUint32(0) === 0x89504e47; + if (!png) problems.push("og.png: missing or not a PNG"); + else if (view.getUint32(16) !== 1200 || view.getUint32(20) !== 630) { + problems.push(`og.png: ${view.getUint32(16)}x${view.getUint32(20)}, expected 1200x630`); + } +} + +// The 404 page, checked as if served at a deep missing path. +{ + const missing = `no-such-page-${Date.now()}/deeper/still.html`; + if (live) { + const { status, text } = await get(missing); + if (status !== 404) problems.push(`${missing}: status ${status}, expected 404`); + if (!text.includes("

    Not found

    ")) problems.push(`${missing}: not answered with the 404 page`); + } + await crawl("404.html", { social: false, servedAs: missing }); +} + +const htmlPages = [...checked].length; +if (problems.length) { + console.error(`${problems.length} problem(s) on ${live ? base : target}:\n ${problems.join("\n ")}`); + process.exit(1); +} +console.log(`${htmlPages} pages checked on ${live ? base : target}: every link, anchor, and meta tag resolves; nothing loads from another origin.`); diff --git a/scripts/og_image.html b/scripts/og_image.html new file mode 100644 index 0000000..bc7619d --- /dev/null +++ b/scripts/og_image.html @@ -0,0 +1,57 @@ + + + + + + + + +
    +

    plumbline

    +

    A calibration claim
    has a floor

    +
    + + +
    + 105 rows: inconclusive, not a pass + tmhsdigital.github.io/plumbline +
    + + diff --git a/site/index.html b/site/index.html index 6f4679e..159f64b 100644 --- a/site/index.html +++ b/site/index.html @@ -5,6 +5,21 @@ plumbline: the ECE floor + + + + + + + + + + + + + + + diff --git a/site/og.png b/site/og.png new file mode 100644 index 0000000000000000000000000000000000000000..273ab27b1027781469827c139661f7ad153fd22b GIT binary patch literal 45665 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 axqIsi)g3)hZ25oEcGZ Date: Tue, 22 Sep 2026 19:45:43 -0400 Subject: [PATCH 2/2] fix(site): match expected meta values literally in the site check CodeQL: the expected og:image URL went into a RegExp with its dots unescaped, so a look-alike host would have passed. Co-Authored-By: Claude Opus 5.5 (1M context) --- scripts/check_site_links.mjs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/scripts/check_site_links.mjs b/scripts/check_site_links.mjs index 711003c..b2d7c46 100644 --- a/scripts/check_site_links.mjs +++ b/scripts/check_site_links.mjs @@ -78,8 +78,9 @@ function references(html) { } const ids = (html) => new Set([...html.matchAll(/\bid="([^"]+)"/g)].map((match) => match[1])); +const literal = (text) => text.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"); const meta = (html, key, value) => - new RegExp(``).test(html); + new RegExp(``).test(html); const metaValue = (html, key) => new RegExp(``).exec(html)?.[1] ?? null;