From 6b6cb545d4cad5a5bdd3b744941c714b130b79d0 Mon Sep 17 00:00:00 2001 From: Matt McKay Date: Mon, 17 Aug 2026 08:51:05 +1000 Subject: [PATCH] Retire 8 stale annotations and make the manifest-or-annotation rule enforceable MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit audit_annotations.yml's header says a migrated dataset does not get a `datasets:` entry — the manifest is its source of truth. Eight datasets violated it: assignat.xlsx, caron.npy, chapter_3.xlsx, dette.xlsx, fig_3.xlsx, longprices.xls, mpd2020.xlsx and nom_balances.npy. Each annotation was diffed against its manifest before deletion. All eight are strictly subsumed: the annotations carry a one-line description and a `provenance` word, while the manifests carry a title, a full description, class, builder_status and a source note. The two hand-prepared .npy files say "no construction record" in prose; their manifests say the same thing machine-readably as `class: constructed` + `builder_status: unrecovered`. The two hand-transcribed spreadsheets say "hand-transcribed by authors"; their manifests quote the consuming lecture saying so. One entry disagreed with its manifest rather than merely duplicating it: mpd2020.xlsx was annotated `provenance: verbatim`, while its manifest records `class: constructed` with `integrity.upstream.status: diverged` and a `local-edit` delta — three header labels on the `Regional data` sheet were renamed locally, and the lecture reads that sheet with header=(0,1,2), so the edits are load-bearing. The annotation was the stale half. That is exactly the rot the rule exists to prevent, and it is why deleting these rather than leaving them inert is worth doing. The rule is now enforced. `dual_recorded` is a fifth key in the audit's problems dict: any filename holding both a lectures/.yml manifest and a datasets: entry. It is computed from the two static sources rather than from the scan, so it fires even for a dataset no lecture currently references, and --strict already fails on any non-empty problems value. Measured in both directions rather than asserted. As committed: exit 0, all five problem keys empty. With a single stale entry re-added: exit 1, one `dual_recorded` warning naming the file and the fix. Restored: exit 0. Not rendered on the dashboard. render_audit.py surfaces only migration_inconsistencies, and a --strict failure skips the deploy entirely, so the workflow run is the surface that carries this signal. Closes #86 Co-Authored-By: Claude Opus 5 (1M context) --- scripts/audit_annotations.yml | 28 ---------------------------- scripts/build_audit.py | 15 +++++++++++++++ 2 files changed, 15 insertions(+), 28 deletions(-) diff --git a/scripts/audit_annotations.yml b/scripts/audit_annotations.yml index 19ada0c..2841d17 100644 --- a/scripts/audit_annotations.yml +++ b/scripts/audit_annotations.yml @@ -22,9 +22,6 @@ datasets: description: ACS occupation summary provenance: constructed-lost note: construction (ACS filters, grouping, sorting) described in lecture prose only - assignat.xlsx: - description: Assignat issuance / price data, French Revolution - provenance: author-assembled bbh_macro_quarterly.csv: description: Macro quarterly series (Bhandari et al.) provenance: constructed-lost @@ -33,24 +30,10 @@ datasets: description: Michigan survey monthly series (Bhandari et al.) provenance: constructed-lost note: same Zenodo replication package; extraction not scripted - caron.npy: - description: French Revolution money balances (Caron) - provenance: author-assembled - note: hand-prepared NumPy array, no construction record - chapter_3.xlsx: - description: Hyperinflation tables, Sargent "Ends of Four Big Inflations" - provenance: author-assembled - note: hand-transcribed by authors dataBHS.mat: description: US consumption/income series, MATLAB replication bundle provenance: verbatim flags: [lectures-root] - dette.xlsx: - description: French government debt series - provenance: author-assembled - fig_3.xlsx: - description: French Revolution fiscal data - provenance: author-assembled fred_data.csv: description: FRED snapshot — GS1, GS5, GS10, DFII5, DFII10, USREC provenance: constructed-lost @@ -71,17 +54,6 @@ datasets: description: Hansen–Jagannathan (1991) asset-returns bundle provenance: constructed-lost note: lecture documents 3 sources (FRED yields deflated by CPIAUCSL, …); no build script - longprices.xls: - description: Long-run price levels (Sargent–Velde) - provenance: author-assembled - note: hand-transcribed by authors - mpd2020.xlsx: - description: Maddison Project Database 2020 (GDP per capita, long run) - provenance: verbatim - nom_balances.npy: - description: Nominal balances, French Revolution - provenance: author-assembled - note: hand-prepared NumPy array, no construction record test_pwt.csv: description: Penn World Table 7.0 extract provenance: author-assembled diff --git a/scripts/build_audit.py b/scripts/build_audit.py index 673a700..26bca89 100644 --- a/scripts/build_audit.py +++ b/scripts/build_audit.py @@ -354,6 +354,15 @@ def scan(repos_dir: Path): manifests = load_manifests() migration = load_yaml(MIGRATION) + # A migrated dataset's manifest is its source of truth, so it must NOT also + # carry an `audit_annotations.yml` entry (that file's own header states the + # rule). Nothing enforced it before: annotations are consulted only as a + # fallback when a manifest is absent, so a stale entry is inert at runtime + # and a green audit said nothing about the invariant. Computed from the two + # static sources rather than from the scan, so it still fires for a dataset + # no lecture currently references. + dual_recorded = sorted(set(manifests) & set(datasets_ann)) + repos, all_refs, all_api, unscanned_nb = {}, [], [], [] for name in SCAN_REPOS: res = scan_repo(name, repos_dir) @@ -540,6 +549,12 @@ def scan(repos_dir: Path): "missing_api_annotations": sorted(api_missing), "migration_inconsistencies": mig_problems, "unscanned_notebooks": sorted(unscanned_nb), + "dual_recorded": [ + f"{f}: has a lectures/{f}.yml manifest AND an " + f"audit_annotations.yml datasets: entry — delete the annotation, " + f"the manifest is the source of truth" + for f in dual_recorded + ], }, "stats": { "static_files": len(datasets),