Repository navigation
VALIDATION: independent review of the 2026-08-07 french_rev / write-back work #51
Description
Everything below was verified during the work by the same agent that did it. This issue exists so it can be checked by someone who was not there.
Bias to test for: almost every integrity claim in this session was produced by one method — an ad-hoc Python script in a throwaway venv (pandas 3.0.5) that fetched the new URL and compared it against lecture-python-intro's old committed copy — plus curl for status codes and headers. The session leaned on that pair because GitHub Actions was in a major outage for much of it, so for a long stretch no build could run at all. Confirm reader-facing outcomes without that pair: fetch the published artifacts, open the notebooks, execute the cells, read the rendered pages. A second bias worth naming: the pre-deletion safety sweep was run over the nine repos cloned in workspace-lectures, and was described in the PR as "org-wide". It was not — see section 6, which already found one live regression from exactly that gap.
What landed
| Repo | PRs merged |
|---|---|
lecture-python-intro |
#826 (repoint), #827 (phase-2 deletion) |
lecture-wasm |
#55 |
data-lectures |
#49 (tracker flip), #50 (repoint rule 6) |
Issues opened: QuantEcon/workspace-lectures#25, QuantEcon/QuantEcon.manual#149, and this one. Comments posted: QuantEcon/QuantEcon.manual#108, QuantEcon/meta#336, QuantEcon/meta#337, QuantEcon/meta#338, #37, QuantEcon/workspace-lectures#23, QuantEcon/workspace-lectures#14. Closed: QuantEcon/meta#337. Publish tag: publish-2026aug07 on lecture-python-intro, which needed three attempts — two failed on Anaconda defaults CDN errors before succeeding with no change to any file.
For the validator
Work in a session that did not do this work. Do not use the tool named under "Bias to test for" except where a check explicitly says to run it. Re-derive counts rather than confirming them. Where a check can be run against a surface the original session did not exercise, do that too — the margin beyond the checklist is where regressions hide. Deliver: one comment on this issue with a per-item verdict (confirmed / confirmed with caveat / refuted / not completable, with evidence), a new issue for any regression found (do not bury findings in the comment), and leave the checkboxes to the issue owner unless told otherwise.
1. Reader-facing outcomes — check these first, without the session's tooling
-
intro.quantecon.org/french_rev.htmlrenders and every figure is present. The lecture plots assignat/dette/fig_3 series and two.npy-derived series — all should look like economics, not empty axes - Download
intro.quantecon.org/_notebooks/french_rev.ipynband run it end to end. Every data cell must succeed - Open the same notebook in Colab and run it. This is the failure this set exists to fix:
caron.npyandnom_balances.npywerenp.load('datasets/…')local-path reads that raisedFileNotFoundErroranywhere the working directory was notlectures/ - Click the three Data Sources links at the top of the rendered lecture — each should reach a file in
data-lectures, not a 404 and notlecture-python-intro - Compare a rendered figure against the pre-migration published version via the Wayback Machine. The bytes are claimed identical, so the figures must be pixel-identical
- Beyond what the session tested: the session never executed
lecture-wasm'sfrench_revin a browser. It only checked the URL form and thatcurlsawaccess-control-allow-origin: *. Openquantecon.github.io/lecture-wasm/french-rev/, wait for Pyodide, and run the data cells. This is precisely the surface that produced Repoint rule for wasm: in-browser reads need raw.githubusercontent.com — the github.com/*/raw/ form fails CORS, breaking long_run_growth and inflation_history on the wasm site #46, and a passing static page proves nothing there — the wasm build bakes no outputs
2. Artifact integrity — the claim is byte-identity
The claim: what data-lectures serves is byte-identical to what the lectures read before, for all five files.
- For each of
assignat.xlsx,dette.xlsx,fig_3.xlsx,caron.npy,nom_balances.npy: fetch fromraw.githubusercontent.com/QuantEcon/data-lectures/main/lectures/<file>and comparesha256againstintegrity.sha256in its sidecar manifest - Triangulate against git history, which is the source the live fetch cannot fake:
git -C lecture-python-intro show 6986e35a~1:lectures/datasets/<file> | shasum -a 256(the commit before #826) must equal the same value - Confirm the recorded sizes: 209,555 / 632,030 / 9,466 / 1,136 / 1,424 bytes
- Re-derive rather than trust: the session claimed all ten
read_excelcalls produceDataFrame.equalsframes. Re-run them from the pre-deletion blobs vs the live URLs, with the lecture's exact arguments. All three workbooks arepositional_reads: true— read by cell position withusecols/skiprows/nrows— so a shifted row would change a figure silently rather than raise - Confirm both
.npyfiles load to arrays of shape(63, 2)and(81, 2), dtypefloat64
3. The highest-value claim to re-test — repoint rule 6
#50 records that media.githubusercontent.com is LFS-only and 404s on plain-git files. All of step 3 (the high_dim_data fold, 6 datasets, 14 reads) is being planned around this. It was measured once, on one file.
- Reproduce from scratch:
curl -I https://media.githubusercontent.com/media/QuantEcon/data-lectures/main/lectures/mpd2020.xlsxshould be 404, and theraw.githubusercontent.comequivalent 200 - Test the converse, which the session did not: pick a file that is LFS-tracked in
high_dim_dataand confirm the media host serves it whileraw.githubusercontent.comreturns pointer text rather than data - Confirm both hosts send
access-control-allow-origin: *, i.e. that this is host routing and not CORS as first assumed - Re-derive the count of 14 affected reads independently, and check the file:line table in
PLAN.mdrule 6 against the current files. Two entries are_static/…/inequality/data.ipynbbuilder notebooks — confirm the audit genuinely does not classify those as data reads - Check the claim that this is invisible to CI: add a throwaway media-host reference to a data-lectures path in a scratch branch of a lecture repo and confirm no check anywhere fails
4. Records written
- All five french_rev manifests parse, and each
filenamematches its sidecar's own name (load_manifests()keys onfilename, so a typo makes a manifest silently invisible) - Each now lists exactly two
consumers—lecture-python-introandlecture-wasm, bothlectures/french_rev.md— and those files really do read them -
positional_reads: trueis set onassignat,detteandfig_3and not on the two.npyfiles -
.github/scripts/check_consumed_files.pyreports 18 manifests, 18 consumed files, 0 errors. Confirm the count rose from 13 because these five gained consumers, and that the check actually hashes bytes rather than trusting the manifest
5. Tracker consistency
-
migration.ymlis 18repointed, 0landed— count record-level entries only; an unanchored grep matches a comment line and returns 19 - Each of the five
repointsentries names the PRs that did the work (#826 and Audit: assert on the media host, and on data-lectures URL resolvability #55) with date 2026-08-07 -
CATALOG.mdis current: runpython scripts/build_catalog.pythengit diff --exit-code -
python scripts/build_audit.py scan --repos-dir ../ --strictexits 0 with all repos atorigin/main - Committed orphans are 26, down from 31 — exactly the five deletions. Re-derive; do not trust
-
lecture-python-intro/lectures/datasets/holds exactly three files:fig_3.ods,GDP_per_capita_world_bank.csv,Metadata_Country_API_NY.GDP.PCAP.CD_DS2_en_csv_v2_4770417.csv
6. Known blind spots — one already produced a live regression
The session documented that the audit cannot see prose references, and added that it cannot see a wrong host either. A third was found while writing this issue and is already confirmed broken, so treat the listed blind spots as a floor, not a ceiling.
- Confirm the regression in QuantEcon/workspace-lectures#25.
lecture-intro.zh-cn'smainreads eight datasets by URL fromlecture-python-intro/lectures/datasets/, all now 404. Verify the eight, and verify the fix exists as the open sync PRs 🌐 [translation-sync] long_run_growth: read mpd2020.xlsx from data-lectures lecture-intro.zh-cn#285, #286 and #288 - Verify the cause rather than the symptom: the pre-deletion sweep ran over the nine repos in
workspace-lectures/manifest.yml, which excludes translation repos, and was described in #827 as "org-wide". Confirm that description is wrong - Re-run the sweep properly — over every non-archived QuantEcon repo, not the workspace clones — and report whether anything besides
lecture-intro.zh-cnreads the five deleted paths. The session's own re-check covered only repos matchingintro|wasmby name, which is itself a guess - Confirm the six french_rev prose references (
french_rev.md:60-62in both repos) were repointed, and that no build in either repo would have caught them if they had not been - Re-derive the claim that these were the last local-path data reads in
lecture-python-intro
7. Decisions settled today — future work builds on these
-
pandas.read_*cannot take a timeout for a URL read, andstorage_options={"timeout": 5}is silently ignored — pandas passesstorage_optionsas HTTP headers (pandas.io.common._get_filepath_or_bufferbuildsurllib.request.Request(url, headers=storage_options)). Re-verify against pandas 3.0.5; QuantEcon.manual#149 rests on it - The
publish-2026aug07failures were upstream, not configuration:git diff publish-2026aug06b..main -- .github/workflows/publish.yml environment.ymlis empty, andsetup-minicondav4.0.1 predates the last green publish. Confirm both - The
high_dim_data"auto-delete head branches" story recorded in QuantEcon/workspace-lectures#14 is false:delete_branch_on_mergeisfalseon that repo, and the four repoints merged before the deliberate branch deletion. Re-check viagh api
8. Deliberately not done
-
fig_3.odswas kept inlecture-python-introwhilelecture-wasm's copy was deleted. Confirm the stated reason holds: wasm's was a redundant mirror, intro's is the last copy in the org becausedata-lecturesdropped it as a format twin offig_3.xlsx. Verify no copy exists indata-lecturesand that nothing references it - Copilot's request for
raise_for_status()and a timeout on #826 was declined rather than overlooked — see the reply thread and QuantEcon.manual#149 -
environment.ymland the conda channel configuration were not changed despite that being the likely way to make the failing publish go green. Confirm this was recorded as a deliberate scope decision
Where the reasoning lives
PLAN.md in this repo carries the six repoint rules and the phase machinery; AGENTS.md the manifest conventions. QuantEcon/workspace-lectures#23 is the ordered plan with per-step traps, QuantEcon/workspace-lectures#14 the standing cross-repo tracker including two corrections made today. The PR descriptions on #826, #55, #49, #50 and #827 carry the per-change evidence and the sequencing arguments.
Activity
mmcky commentedon Aug 7, 2026
Update before validation begins — section 6's regression is resolved, and it grew a second finding.
The regression this issue points to (QuantEcon/workspace-lectures#25) is now closed. All three translation sync PRs are merged — QuantEcon/lecture-intro.zh-cn#285, #286, #288 — and zh-cn's
mainno longer referenceslecture-python-intro/…/lectures/datasets/anywhere; all eight datasets serve 200 from data-lectures.Two adjustments to section 6, both of which make it a stronger check rather than a weaker one:
The validator should still run the org-wide sweep. The cause is unchanged: the pre-deletion sweep covered
manifest.yml's nine repos and was described in QuantEcon/lecture-python-intro#827 as "org-wide". The fact that the symptom is fixed says nothing about whetherlecture-intro.zh-cnwas the only repo affected — that was never established, and the follow-up check only looked at repos whose names matchintro|wasm, which is itself a guess.A new blind spot to verify, found while fixing #285. The sync PR overwrote a deliberate localisation: it replaced this repo's
code_to_name = pd.read_csv("../lectures/datasets/country_code_cn.csv")with the English source's version, dropping thename_chinesecolumn that nine call sites depend on. Had it built green, every chart inlong_run_growthwould have reverted to English country labels on the Chinese site. It failed loudly only by luck — a downstream cell indexesname_chineseby name and raisedKeyError; a localisation affecting only rendering would have merged silently.Worth adding as checks:
main(long_run_growth.md:141) and that:263still readsname_chineseThis strengthens rather than weakens the "Bias to test for" framing at the top: the session's sweep was scoped to its own workspace, and both findings in this section came from looking outside it.
mmcky commentedon Aug 7, 2026
Independent validation — per-item verdicts
Run in a session that did none of the 2026-08-07 work, against
data-lecturesa935449,lecture-python-intro7694e3ec andlecture-wasmeceed27 (all clean, all atorigin/main). Read-only throughout: no writes under any repo, no GitHub mutations. Where a repo's own script had to run, it ran against acp -acopy in scratch.Result: 26 confirmed, 18 confirmed-with-caveat, 3 refuted, 1 not-completable. 45 candidate findings were raised; each was put through an adversarial pass whose job was to refute it, and 21 did not survive — they are not reported here.
On the named bias
The instruction was to avoid the session's fetch-script-plus-curl pair. The substantive checks were re-derived by methods that pair cannot produce:
cat-file, blob-SHA identity, entirely offlinestorage_optionsclaimThree checks could not be run as written and were substituted, declared here rather than quietly: Colab (needs an interactive Google sign-in — substituted execution from a bare directory with no
datasets/, which is the condition Colab creates); the CI-invisibility test (would have required pushing a scratch branch — substituted a local injection into a scratch clone, with a positive control); Wayback (see §1).1. Reader-facing outcomes
french_rev.htmlrenders, every figure present18
<img>in<main>, all complete, none broken — and the count is right: 15plt.figure()+ 3plt.subplots()= 18plt.show()= 18 images, so no figure silently vanished. Against a locally generated blank-axes control (nonwhite_frac0.0183, 0 coloured pixels), every published figure sits far above it (0.033–0.078 nonwhite, 338–1853 distinct colours). Four figures were re-rendered from the data-lectures bytes and matched to the published PNGs by block-density correlation: 0.83–0.90, against 0.06–0.12 for unrelated-figure controls.The notebook executes clean from a bare directory with no
datasets/— which is the actual substance of the Colab check. 60 cells, all data cells succeed.The wasm check — the surface the session never exercised — passes, and passes properly. All 22 cells ran in a real Pyodide runtime (kernel ready ~5 min). Every dataset fetch returned 200 from
raw.githubusercontent.com, zero CORS console errors, and SHA-256 computed inside the browser matches the git blobs for all five files. The negative control is what makes this meaningful: the other host form,github.com/QuantEcon/data-lectures/raw/…, throwsTypeError: Failed to fetchwith an explicit CORS block from that same origin. So rule 5's host distinction is real, and wasm is on the correct side of it.Wayback is not-completable by construction, not by accident. The Wayback Machine holds
french_rev.html(9 captures) but has never archived a single one of its figure PNGs — CDX returns empty for all 15, andid_fetches 404 on 15 of 15. The newest pre-migration capture is also 2026-03-09, five months stale and predating two commits that changed the figure set, so it would have been a contaminated baseline regardless. The substantive claim was re-derived another way and holds: the build artifact frompublish-2026aug06(last pre-migration build) and from the migration commit reference an identical list of 18_images/*.png, and all 18 are sha256-identical. Jupyter Book names each PNG by the sha256 of its own bytes, so identical filenames are byte identity.The Data Sources caveat: the three links are correct, 200, no redirects, and resolve to blobs that match
git cat-file— but only three of the five migrated datasets are documented.caron.npyandnom_balances.npyare fetched atfrench_rev.md:711-712and appear nowhere in the prose. Nine of the eighteen figures derive entirely from those two files. The checklist's own wording ("the three Data Sources links") bakes the gap in.2. Artifact integrity
6986e35a~1read_excelframesDataFrame.equalsread_as.npyshapes and dtypeThis section is the strongest in the issue. Byte-identity survives four independent chains — offline git blob identity, the contents API, the raw CDN, and the
github.com/*/raw/form the lecture actually uses — and all four agree on all five files.6986e35awas verified to be #826's merge commit, touching onlylectures/french_rev.md, so~1genuinely is the pre-repoint tree. Sizes match acrossgit cat-file -s,wc -cand the API. Both arrays are exact:(63,2)and(81,2),float64, bitwise-identical old blob vs live, and the load-bearing cross-file alignmentnom_balances[14:77,1] * caron[:,1]reproduces.The caveat is a counting error, not a substance error: there are 13
read_excelcalls, not ten — ten distinct argument sets, three of which are used twice. All 13 return.equals() == True, under both pandas 2.3.3 and 3.0.5. Manifestread_asentries map 1:1 onto the ten distinct sets with nothing undocumented and nothing unused.3. Repoint rule 6
access-control-allow-origin: *PLAN.mdtableRule 6 now rests on n=12 rather than n=1. Seven plain-git files 404 on the media host — including two inside
high_dim_dataitself, which proves this is per-path LFS tracking and not a repo-level property — and five LFS files serve real bytes on media while raw returns 130-byte pointer text with a 200, a silent parse failure rather than an error. Both hosts sendaccess-control-allow-origin: *, including on the 404, and a browser fetch from a real page origin reads every status without throwing: the media 404 is a genuine routed 404, not a CORS block. Host routing, as rule 6 says.The count of 14 reproduces exactly, and every
file:linein thePLAN.mdtable matches byte-for-byte. The builder-notebook question checks out:build_audit.py:216selects onlylectures/**.mdand:222excludes/_static/, andaudit.jsonlists exactly the 14 minus those two.Both caveats are scope, not correctness. 14 is workspace-scoped —
lecture-intro.zh-cncarries 7 more live media-host reads that the step-3 fold will also have to move. And the CI claim is confirmed with one correction:build_audit.py scan --strictexits 0 with a media-host URL injected (while cheerfully recordingurl_form: media, lfs_media: Trueand never asserting on it), andcheck_consumed_files.pyexits 0 because it never looks at consumer URLs at all — but the framing "no check anywhere" overstates slightly, since intro'sci.ymldoes executelectures/*.md, so an actually-broken read would fail there even though the host defect itself is invisible. The positive control confirms the negative is real: flipping wasm togithub.com/*/raw/gives exit 1 with 5 rule-5 warnings.4. Records written
filenamematches sidecarpositional_readson the three workbooks, not the.npyAll 18 sidecars parse, all filenames match, no duplicate keys. Consumers verified in both directions, including by execution against both URL forms. The 13→18 delta was re-derived structurally from
git showat four revisions and is exactly the five french_rev files. Hashing is real — flipping one byte ofcaron.npyproducesbytes do not match the manifestand exit 1.Two caveats worth carrying forward. The parenthetical rationale is wrong: a
filenametypo is not silently invisible — it produces::error:: … does not match the sidecar's own nameand exit 1 (verified by mutation), and onlybuild_audit.pykeys onfilename;build_catalog.pyreturns a list. More materially,check_consumed_files.pyhashes the repo's own committed copy against the manifest — no URL, no host, no network. So "0 errors" says nothing about what the lectures actually fetch; the exact class of regression rules 5 and 6 exist to prevent would leave it green. And "18 manifests" is 18 of the 19 datasets inlectures/—business_cycle_data.csvhas no sidecar and is invisible to the check.positional_readsis absent on the two.npymanifests rather thanfalse, so the schema cannot distinguish "not positional" from "not yet assessed".5. Tracker consistency
repointed, 0landedCATALOG.mdcurrentbuild_audit.py scan --strictexits 0lectures/datasets/holds exactly three filesParsed rather than grepped: 18 records, all
repointed, zerolanded. The asserted grep trap is real — the unanchored form returns 19, the 19th match being the comment atmigration.yml:341.CATALOG.mdregenerates byte-identical. The strict scan exits 0, and it is not vacuous: re-run against the pre-session tree it correctly emitsnom_balances.npy: marked repointed but consumers still read via [local-path…]. The three surviving files inlectures/datasets/were confirmed four ways, including--ignoredto catch strays.Orphans: 26 now, 31 before, and the delta is the five files by name, not merely by count. The caveat is that 31 is the intermediate state. The true pre-session baseline was 32 — wasm#55 also deleted six already-orphaned mirrors — so the real movement was 32 → 31 → 26.
The date caveat: both PRs merged
2026-08-06T23:46Zand23:49ZUTC.2026-08-07is the local AEST date.migration.ymlstates no timezone convention.6. Blind spots
:263readsname_chineseThe cause is confirmed, and PR #827's wording is worse than this issue says. Verbatim from #827: "An org-wide sweep over every repo's
origin/mainfinds no remaining reader of these five paths. The only matches are data-lectures' own sidecar manifests and its audit annotations, neither of which is a read." Not just the label — the conclusion was false at the moment it was written. #827 merged at01:35:54Z; zh-cn'smainwas then0466562, which read three of the five paths, and was not superseded until 17 minutes later.The properly-scoped sweep: 253 non-archived repos enumerated, 252 shallow-cloned (only
QuantEcon/.githubis unclonable), all grepped. Clone-and-grep rather than code search, which does not index URL strings reliably.Within live lecture series and their downstream dependents,
lecture-intro.zh-cnwas the only reader, and it is fixed. The.notebooksmirrors (intro, python, programming, advanced — all pushed within the last week) are clean;lecture-python-intro.notebooksalready carries the data-lectures URLs. The other translations never read these files.lecture-datascience.mystreads none of them.Two repos outside that boundary still resolve the deleted paths, and they are executable reads, not prose links — reported below rather than buried here.
The zh-cn caveats are all overcounts in the issue's own wording. "Eight datasets" is eight lines resolving to six distinct files;
caron.npyandnom_balances.npywere read from zh-cn's own committed copies by local path and never 404'd. "Nine call sites depend onname_chinese" is really one —:263— with six more passingcode_to_nameinto that helper.long_run_growth.md:141and:263are exact, no drift, and the CSV's 27 rows resolve every code the lecture uses; the reverted alternative reproducesKeyError: 'name_chinese'first-hand. "#286 and #288 touched URL lines only" is materially wrong for #288, which also rewrote twonp.loadcalls intorequests+BytesIOand added imports — though the load-bearing point (neither overwrote a localisation) stands."No check anywhere detects an overwritten localisation" is false as worded, and worse in substance: a purpose-built deterministic guard does exist in
action-translation(src/cli/target-local-reads.ts), written three weeks earlier for this exactcountry_code_cn.csv/name_chinesecase — but it is wired only into the CLI'sforwardcommand and is absent from the action bundle CI runs (findTargetLocalReads,verifyPreservedReads,buildPreserveInstructionall grep to 0 hits indist-action/index.js). Nothing protected this sync because the protection was never shipped to the path that needed it.The prose check confirms cleanly — 6 references, 3 per repo, lines 60-62, all now 200, verified from git blobs and from both published sites. "No build would have caught them" was demonstrated rather than argued: a jupyter-book built with intro's exact
-nW --keep-goingflags and a deliberately-404 link reports nothing, andmyst build --htmllikewise without--check-links, which wasm's CI never passes. One correction — intro does havelinkcheck.yml, but it is cron-only,fail-on-broken: false, and runs over the release tarball, so it files an issue rather than failing a build."Last local-path data reads" is confirmed for datasets — 0 local-path reads in either repo, and the detector is live (it still reports 4 for
lecture-python-advanced.myst). Strictly there is one remaining localopen():short_path.md:463readsgraph.txt, which the same lecture writes at:272via%%file. The audit classifies this asembedded, correctly.7. Decisions settled
storage_optionstimeout silently ignoredhigh_dim_databranch story is falseThe pandas claim is confirmed against pandas 3.0.5 itself, and proved rather than read:
io/common.py:403-404is verbatim as quoted, a local echo server receivesTimeout: 5andX-Probe: helloas literal HTTP headers, and against a server that accepts and never replies the read blocks past 25s identically with{"timeout": 5},{"timeout": "5"}andNone— no timeout effect, no warning for the unknown key. QuantEcon.manual#149 rests on solid ground.Both halves of the publish claim verify — the diff is empty (confirmed by blob identity across all three refs, not just diff text), and
setup-miniconda@v4resolves to v4.0.1's SHA in all four log bundles, released 2026-04-24, before the last green publish. But "upstream, not configuration" misnames the surface. Both failures died fetchinghttps://conda.anaconda.org/default/linux-64/repodata.json— channeldefault, singular, which is whatenvironment.ymlliterally declares. That is not conda'sdefaultsalias; it is a real but empty anaconda.org user channel returning{"packages":{}}in 160 bytes.defaults— served fromrepo.anaconda.com— succeeded in every attempt, with ToS accepted mid-failure. All 20 timeout errors in attempt 1 nameconda.anaconda.org; none namerepo.anaconda.com.Section 7's third item is refuted. The first conjunct holds (
delete_branch_on_merge: false, so the deletion was manual). The second is backwards: the Events API recordsDeleteEvent ref=update_scf_noweightsat2026-07-15T23:57:24Z, 12 seconds after the merge — and the four repoints merged 19 to 24 minutes later (00:16:19Z,00:16:22Z,00:20:17Z,00:21:07Z). So the branch was gone before the consumers were repointed, not after. The ws#14 correction's conclusions — "no consumer ever 404'd" and "the ordering rule was followed here, not violated" — are the reverse of the timeline.8. Deliberately not done
fig_3.odskept; intro's is the last copy in the orgenvironment.ymluntouched, recorded as a deliberate scope decisionThe Copilot item is clean and well-evidenced: the request landed
23:23:08Z, the reply declining it23:42:41Zwith three stated reasons, and the merge23:46:08Z— a decision predating the merge by 3m27s, not an oversight. The corpus claim it rests on re-derives exactly:requests.get8 hits,raise_for_status0 across all nine clones."The last copy in the org" is false. A Trees-API scan of all 277 org repos on their default branches finds blob
5d6ae11con four non-archived repos:lecture-python-intro,lecture-intro.zh-cn(pushed 2026-08-07),test-actions-lecture-introandpython-lecture-sandpit.myst. The other three sub-claims hold — wasm's was a redundant unreferenced mirror, data-lectures holds none, and the format-twin evidence re-measures correctly (both parse to oneSheet1,DataFrame.equalstrue) — but the one the retention decision rests on does not."Confirm this was recorded" fails.
environment.ymlgenuinely is unchanged — blob-identical acrosspublish-2026jul17,aug06,aug06b,aug07andmain. But no record of the scope decision exists outside this issue. A sweep of every issue and PR comment since 2026-08-06 acrosslecture-python-intro,lecture-wasm,data-lectures,workspace-lectures,QuantEcon.manualandmeta(~250 KB) returns zero hits forconda/environment.yml/channel; #826 and #827 never mention publish or conda. The only written trace is #51 — the artifact under validation, written by the same agent.Findings
Filed separately rather than buried here, per the brief. Everything below survived an adversarial pass; the corrections that pass forced are already applied.
Inside the lecture repos:
channels: - default(singular, empty channel) in seven workspace repos — the endpoint both failed publish attempts died onlecture-python-intro+ 6 siblings%piprenders a red error block as the first visible output on all 14 wasm lectures that have onelecture-wasm/myst-theme.cssrequested withoutBASE_URL, 404 on every page (10/10 sampled)lecture-wasmFutureWarnings printed to readers ('M'→'ME','AS'→'YS') — already fixed upstream in introlecture-wasmfrench_rev.mdfr_fig3on the published pagelecture-python-introfig_3.odsretention rests on a false premise (4 org copies, not 1)lecture-python-introworkspace-lectures#14assignat.xlsx.yml:97says 14 unread sheets, lists 15data-lecturesbuild_audit.pycommitted_referenced()basename fallback is unreachabledata-lecturesTranslation programme (auto-synced, so out of this workspace's hand-sweep, but the guard gap is why ws#25 happened): the target-local-read guard is not compiled into the action bundle and not wired into
syncmode; it also cannot see a target-local read expressed as a URL, which is howlecture-python.zh-cn'scountry_map.csvis written.rebase-translations.ymlhas been dead inlecture-intro.zh-cnsince 2026-07-21 —QUANTECON_SERVICES_PAT's selected-repository list does not include it. zh-cn still commits eight orphaned dataset copies. The forward sweep found 15 collision rows, 4 high, the sharpest beingheavy_tails.md:862, where the read cell also holdscountry_names = ['美国','日本','印度','意大利']consumed at:881— and P3's media→raw rewrite must edit exactly that line.Outside live lecture series and their dependents — reported for triage, no action taken:
QuantEcon/tom-econ370-2025setsbase_urlto the deleted intro path atlectures/french_rev.md:70-75and drives 13read_excelcalls from it;french_revis in its_toc.yml, Pages is live, and the published page serves three 404 links today. Last push 2025-02-04.QuantEcon/test-actions-lecture-introhas the same reads pluslong_run_growth.md:89, but its_toc.ymlwas trimmed to five canary lectures on 2026-08-05 withonly_build_toc_files: true, so they are latent — they go red the moment the full book is restored, which is the false-alarm mode that trim exists to prevent.Ten non-archived repos carry lecture content, have not been pushed in over 12 months, and all ten have Pages enabled. Separately, 24 repos outside the workspace's nine read data by URL from a QuantEcon lecture repo — the standing blast radius of every future repoint. The
.notebooksmirrors and translations self-heal; the course and workshop repos do not, and nothing watches them.Checkboxes left for the issue owner.
mmcky commentedon Aug 7, 2026
Validation complete, and every finding is now routed. Closing.
33 of 37 checkboxes ticked. The four left open are not outstanding work — each has a settled disposition:
french_rev.htmlbut has never archived a single one of its figure PNGs — CDX empty for all 15,id_fetches 404 on 15 of 15. The newest pre-migration capture is also 2026-03-09, predating two commits that changed the figure set, so it would have been a contaminated baseline regardless._imageslist, all 18 sha256-identical. Pixel-identity confirmed.high_dim_dataauto-delete storyfig_3.odslast copy in the org5d6ae11con four non-archived repos.environment.ymlrecorded as deliberateWhere the findings went
Routing index at QuantEcon/workspace-lectures#27. In short: #607 widened from 3 editions to 7 repos with the publish-failure evidence attached; the 15-row translation collision table posted to QuantEcon/workspace-lectures#26, which it answers; five more archive candidates added to QuantEcon/workspace-lectures#18; piplite severity evidence to QuantEcon/lecture-wasm#42; the PAT grant gap to QuantEcon/meta#319; and five new issues — QuantEcon/action-translation#253, QuantEcon/lecture-wasm#56, QuantEcon/lecture-wasm#57, QuantEcon/lecture-python-intro#828, #52.
What this exercise was worth
Two things justify the format, and one qualifies it.
The named bias was the right thing to name. Re-running the checks by different methods — git object store, a real browser executing Pyodide, build artifacts, local socket servers, the Trees API — is what produced all three refutations. None of them would have surfaced by re-running the original session's fetch-and-compare.
The sweep scope mattered more than the sweep. #827's claim was not merely mislabelled "org-wide"; its conclusion was false at the moment it was written. Re-running it properly over all 253 non-archived repos found two further repos that still resolve the deleted paths. The lesson for future sweeps is to scope explicitly to live lecture series plus downstream dependents, and to clone-and-grep rather than trust
gh search code, which cannot find URLs.The qualification: the adversarial pass discarded 21 of 45 candidate findings, and corrected two that survived — including one of this validation's own claims that rested on a 27s latency measurement which turned out to be 0.3s. A validation run without that pass would have shipped a materially wrong finding into a public issue. Worth keeping in the template.