From bb033b7e2faa547358cae90ec38b5115c6921b4d Mon Sep 17 00:00:00 2001 From: Matt McKay Date: Thu, 6 Aug 2026 21:13:28 +1000 Subject: [PATCH 1/2] Audit: fail strict on wasm reads via the github.com/*/raw/ URL form MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Makes repoint rule 5 machine-checked: lecture-wasm executes code cells in the reader's browser, where the github.com/*/raw/ redirect fails CORS, so any wasm code-cell read via a github.com form is now a migration inconsistency — it fails --strict and renders in the dashboard's problems box. Only code-cell reads are scanned, so {download} and prose links (navigations, CORS-exempt) cannot trip it. This is a post-merge net, not a gate: the scan reads each lecture repo's main, so a violation turns the weekly/push audit red rather than blocking the offending PR. PLAN rule 5 updated to match. Negative-tested against a doctored lecture-wasm clone whose origin/main reverts mpd2020.xlsx to the github.com form: strict exits 1 with "mpd2020.xlsx: lecture-wasm reads via github.com/{org}/{repo}/raw/{ref}/… — fails CORS in the browser (repoint rule 5)". Current real repos pass clean. Follows up #46/#47; the empirical basis is recorded there. Co-Authored-By: Claude Fable 5 --- PLAN.md | 2 +- scripts/build_audit.py | 14 ++++++++++++++ 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/PLAN.md b/PLAN.md index 02e815f..e43dd2e 100644 --- a/PLAN.md +++ b/PLAN.md @@ -113,7 +113,7 @@ Learned from the independent validation ([#45](https://github.com/QuantEcon/data | CPython — intro site notebooks, Colab, every other series | any resolving form; `github.com/…/raw/` is fine | | Browser — `lecture-wasm` code-cell reads | `raw.githubusercontent.com/QuantEcon/data-lectures/main/lectures/`, or `media.githubusercontent.com/media/…` for LFS-tracked files | -`{download}` targets and prose links are plain navigations — CORS does not apply, and the `github.com` form is fine there. The audit classifies references by org/repo across all URL forms, so both spellings count as the same pattern: the strict check **cannot enforce this rule**; the repoint PR has to. Quick test from any `quantecon.github.io` page console: `fetch('')` — the bad form rejects, the good form resolves. +`{download}` targets and prose links are plain navigations — CORS does not apply, and the `github.com` form is fine there. The audit classifies references by org/repo across all URL forms, so both spellings count as the same pattern — but the strict build now also checks the *form*: any `lecture-wasm` code-cell read via a `github.com/…` URL fails the audit. That is a **post-merge net, not a gate** — the scan reads each lecture repo's `main`, so a violation turns the dashboard red at the next audit run rather than blocking the offending PR; the repoint PR remains the place the rule is actually upheld. Quick test from any `quantecon.github.io` page console: `fetch('')` — the bad form rejects, the good form resolves. Phase 4 inherits the requirement: `data.quantecon.org` must serve `access-control-allow-origin: *` before `lecture-wasm` can cut over to it — recorded as an acceptance criterion on [#37](https://github.com/QuantEcon/data-lectures/issues/37). diff --git a/scripts/build_audit.py b/scripts/build_audit.py index aa8ff50..f2e4d92 100644 --- a/scripts/build_audit.py +++ b/scripts/build_audit.py @@ -457,6 +457,20 @@ def scan(repos_dir: Path): f"{fname}: in pending wave {wave.get('pilot')} but already " f"read from data-lectures") + # Repoint rule 5 (PLAN): lecture-wasm executes code cells in the reader's + # browser, where the github.com/*/raw/ redirect fails CORS before it is + # followed — wasm reads must use raw.githubusercontent.com, or + # media.githubusercontent.com for LFS files. Only code-cell reads are + # scanned, so {download} and prose links (navigations, CORS-exempt) can + # never trip this. + for d in datasets: + for r in d["refs"]: + if (r["repo"] == "lecture-wasm" + and (r.get("url_form") or "").startswith("github.com/")): + mig_problems.append( + f"{d['file']}: lecture-wasm reads via {r['url_form']} — " + f"fails CORS in the browser (repoint rule 5)") + audit = { "generated": date.today().isoformat(), "repos": {n: {"sha": repos[n]["sha"], From ee185edc4c8b28e2a6cb4565705ae6052ebd5025 Mon Sep 17 00:00:00 2001 From: Matt McKay Date: Thu, 6 Aug 2026 21:53:20 +1000 Subject: [PATCH 2/2] Name the lecture in the rule-5 warning MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Addresses Copilot review: with only the file named, two wasm lectures reading the same file via a bad form would produce identical messages. Negative test now reports "mpd2020.xlsx: lecture-wasm long_run_growth reads via github.com/{org}/{repo}/raw/{ref}/…". Co-Authored-By: Claude Fable 5 --- scripts/build_audit.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/scripts/build_audit.py b/scripts/build_audit.py index f2e4d92..c17f985 100644 --- a/scripts/build_audit.py +++ b/scripts/build_audit.py @@ -468,8 +468,9 @@ def scan(repos_dir: Path): if (r["repo"] == "lecture-wasm" and (r.get("url_form") or "").startswith("github.com/")): mig_problems.append( - f"{d['file']}: lecture-wasm reads via {r['url_form']} — " - f"fails CORS in the browser (repoint rule 5)") + f"{d['file']}: lecture-wasm {r['lecture']} reads via " + f"{r['url_form']} — fails CORS in the browser " + f"(repoint rule 5)") audit = { "generated": date.today().isoformat(),