You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Everything below was verified during the work, by the same agent that did the work. That is the bias this issue exists to break. Track B of the datasets migration completed today across two waves and two sessions — B1′ (the ols trio, fp.dta, NEWQDATA.csv) and B2′ (the hansen_singleton pair and their builders) — and both were checked with one set of tools, one shell, one mental model.
Bias to test for
The monoculture is curl from a single macOS shell plus bespoke Python extractor scripts written by the agent that made the claims. Nearly every reader-facing conclusion — "the published page is correct", "the repoint changed no figure", "the deleted file is still served" — rests on a content_numbers() regex extractor written during the session. That extractor produced a wrong answer twice and was patched twice: once it reported IDENTICAL ✅ against files it had never opened, and twice it reported hundreds of spurious differences that were two number streams knocked out of step by the page's own changelog widget. A third undetected flaw in it would silently confirm exactly what the session wanted to hear.
A second, quieter monoculture: every builder reproduction ran under the agent's local Python 3.13.9 / pandas 2.3.3, not under any pinned project environment. The builder_status: committed and integrity.upstream.status: verified claims both rest on that unpinned run.
Validator: confirm reader-facing outcomes without that toolchain. Open the published pages in a browser and read the rendered tables. Fetch from a different machine and network. Re-derive hashes with a different utility. Do not re-run the session's extractor except where a check says to.
Publishes: lecture-python.mystpublish-2026aug13 and publish-2026aug13b; lecture-python.zh-cn the same two; lecture-statspublish-2026aug13.
Issues: QuantEcon/workspace-lectures#41 opened (build-cache prune); QuantEcon/workspace-lectures#40 body corrected and commented; QuantEcon/workspace-lectures#39 resume block and title rewritten.
1. Reader-facing outcomes
The clearest known incident to confirm: wave B1′'s five files were deleted at 06:31:37Z and a successful publish finished at 09:34:28Z, and the session claims they are STILL SERVED. That is the load-bearing observation behind QuantEcon/workspace-lectures#41.
Fetch each of https://python.quantecon.org/_static/lecture_specific/ols/maketable{1,2,4}.dta, .../mle/fp.dta, .../phillips_drifts_volatilities/NEWQDATA.csv from a machine that is not the one that did this work. Record status, content-type and actual downloaded byte count. Expected today: 200, with 14466 / 11210 / 12024 / 1024085 / 13738 bytes. Confirm a never-existed sibling path 404s in the same run.
After the next cache.yml clean rebuild (Mon 2026-08-17 03:00 UTC) and a publish following it, re-fetch the same five. Expected: 404. Done 2026-08-14, three days early — the rebuild was dispatched manually (run 31757133369) and publish-2026aug14 followed; all five, plus B2′'s six, now 404 with a never-existed control 404ing and ols.html 200ing in the same pass. The model held, so no new issue. Original wording follows: If any still 200s, the session's model of the trap is incomplete — open a new issue.
Open https://python.quantecon.org/hansen_singleton_1982.html and .../hansen_singleton_1983.htmlin a browser and confirm the estimate tables and figures render with numbers, not errors or blanks. The data cell is hide-cell, so a failed fetch surfaces only downstream.
Do the same for https://quantecon.github.io/lecture-python.zh-cn/hansen_singleton_1982.html and 1983.html.
Beyond what the session tested: the zh-cn site publishes downloadable notebooks into its own Pages output at /_notebooks/, and the session never checked that tree. Fetch https://quantecon.github.io/lecture-python.zh-cn/_notebooks/hansen_singleton_1982.ipynb and 1983.ipynb and confirm they carry data-lectures URLs and no _static/lecture_specific/hansen_singleton reference.
Beyond what the session tested: open one of the repointed notebooks in Colab from the published page's own launch button and run the data cell. CPython-in-CI passing does not prove the reader's runtime resolves the URL.
Fetch the published PDF for lecture-python.myst and search its URI annotations for lecture_specific/hansen_singleton and for lecture-python.myst/raw. Note strings is invalid on a compressed PDF — decompress the streams. Expected: no old-form references.
2. Artifact integrity
Two datasets moved. The claim is byte-identity between what the lectures read before and after.
Re-derive both hashes with a utility other than shasum (e.g. openssl dgst -sha256, or Python hashlib) after fetching https://github.com/QuantEcon/data-lectures/raw/main/lectures/hansen_singleton_1982_data.csv and ..._1983_data.csv. Expected 3e9d4f37c31dbcab26624418f3eeb630d57cc362bd162fa5501cfd992225609b (11,662 B) and dc5c1f8dac4b50abb3613dd297f6c22b4ebde80b9a7f02971e8d986b0c6eff18 (26,084 B), matching each manifest's integrity.sha256.
Triangulate against a third source the session did not use as the gate: extract the same two files from lecture-python.myst at commit a501f03 (before #1037 deletes them) and from lecture-python.zh-cn@main, and confirm all three copies hash identically. A divergence in the zh-cn copy would mean the translation's readers were served different bytes than the English readers.
Verify the claim that hansen_singleton_1982_data.csv is a bitwise strict subset of the 1983 file — identical date index, and gross_real_return / gross_cons_growth identical to the last digit. Do it textually (cut the columns and diff) rather than with pandas .equals(), which is how the session checked it.
Confirm both files are plain git, not LFS: git show main:lectures/hansen_singleton_1982_data.csv | head -1 must be a CSV header, not version https://git-lfs....
3. The highest-value claim to re-test
builder_status: committed and integrity.upstream.status: verified both rest on one unpinned local run. More future work depends on this than on anything else today: it is the difference between committed and committed-frozen, it licenses editing the builders, and it is the evidence recorded in both manifests.
Re-run builders/hansen_singleton_1982_data.py and builders/hansen_singleton_1983_data.pyunder the project's pinned environment, not an ambient one, and confirm each reproduces its committed CSV byte for byte. If it does not, the manifests' verified status is wrong and needs correcting — that is a finding, not a nuisance.
Re-run them again a day or more later. Both fetch live upstreams (FRED, the Ken French library). The session's claim is that the 1959–1978 window is stable; a divergence would mean verified has a shelf life the manifests do not state.
Prove the fetch is real, not cached: run with the network disabled and confirm both fail rather than silently producing output.
Confirm each validate() actually rejects. Feed each a deliberately corrupted frame — drop a row, multiply by 100, subtract 1, insert a NaN, rename a column, remove a mid-series month, and for 1983 copy gross_real_return over gross_real_tbill. All seven must raise.
Corroborate the provenance narrative from a source the session did not read: confirm the Ken French F-F_Research_Data_Factors_CSV.zip header still names a CRSP database vintage and Ibbotson for the pre-2024 T-bill leg, and check whether the data library publishes any terms or usage page linked from its main page. The manifests assert it states no licence at all, based on grepping one fetched HTML page — a linked terms page would refute that and change redistribution: permitted from a reasoned call into an unchecked one.
4. Records written
Validate lectures/hansen_singleton_1982_data.csv.yml and ..._1983_data.csv.yml field-by-field against manifest-schema.yml — every required field present, no invented fields, builder_status and integrity.upstream.status from the allowed sets.
Re-derive every schema claim from the bytes rather than trusting it: column names and order, float64 dtypes, row_count_floor: 239 as an exact count, date_range 1959-02-28 to 1978-12-31, known_nulls: {} genuinely empty, and the observed ranges quoted in the column descriptions (returns 0.865–1.159, growth 0.967–1.033, inflation 0.995–1.024, per-capita 2.14e-4–2.66e-4, T-bill 0.984–1.008).
Confirm consumers lists exactly the repos that read each file today, and that no listed file path is stale.
Confirm the two hansen_singleton entries were removed from scripts/audit_annotations.yml and that no dataset now carries both an annotation and a manifest.
Check that builders/README.md's counts are re-derivable from the manifests, not asserted: 21 constructed, 14 with a builder (9 committed + 5 committed-frozen), 12 distinct builder paths, 7 unrecovered, and 13 builder files on disk. These were corrected today after Copilot flagged them; one of them had been wrong since before this wave.
5. Tracker consistency
On main, run the strict audit and confirm exit 0 with no migration_inconsistencies warnings.
Flip each of the two hansen_singleton entries in migration.yml to landed in a scratch checkout and confirm the strict audit fails. A tracker that cannot go red is not evidence when it is green.
Regenerate CATALOG.md and confirm zero diff. Confirm it reports 33 datasets, 33 read by lectures today, and that both new rows show constructed, ✅ verified, ✅ committed, and a non-empty Used by column.
Confirm migration.yml records all seven Track B datasets at status: repointed with cutover: null, and that every repoints[].pr names a PR that is genuinely merged.
6. Known blind spots
The session documented three. Verify each blind spot is real, and re-derive the counts — and note that the sweep producing a count often shared the blind spot it documents.
lecture-python.zh-cn is excluded from build_audit.py's SCAN_REPOS, so no automated check ever sees its reads. Confirm the exclusion, then confirm by hand that both zh-cn lectures read data-lectures on main and that this is recorded in the manifests' consumers — because if it is not recorded there, it is recorded nowhere.
The consumer sweep argued that because the files were created 2026-06-26, only the 88 of 277 org repos pushed since could reference them. Re-derive both numbers. Then test the argument's limits: the sweep covered default branches only, and the QuantEcon org only. Check non-default branches on the plausible repos, and check outside the org — wave B1′ found three live third-party course sites referencing python.quantecon.org/_static/lecture_specific/mle/fp.dta, so the assumption that consumers are in-org is known to be false for this family.
gh search code cannot match a bare URL and cannot see archived repos. Confirm this independently, and confirm the session's mitigation (Trees-API enumeration) actually covers archived repos.
The session's sweep loop silently dropped one repo because a file's last line lacked a trailing newline — caught only by comparing wc -l against grep -c ''. Re-run the sweep with a method that cannot truncate, and confirm the total is 88, not 87.
7. Decisions settled today
D3 — committed, not committed-frozen. Re-test per section 3.
The cache trap affects 9 of 11 repos. Re-derive from each repo's ci.yml, publish.yml and cache.yml on origin/main — the session read local clones, which can lag. Confirm lecture-wasm is the only immune one, and confirm lecture-dp's build-cache key is built only from environment-file hashes so content changes never invalidate it.
jb clean --html removes only _build/html and preserves .jupyter_cache. Verify against the pinned jupyter-book version actually used by CI, and confirm that version is what CI installs.
The historical trap instance. Confirm us_adult_heights.csv and japan_population_by_age.xlsx are present in the publish-2026aug06 and publish-2026aug07 release tarballs for lecture-python-intro and absent from publish-2026aug11.
lecture-stats is not a B2′ consumer. Re-check against origin/main, with a positive control proving the search pattern matches when present.
8. Deliberately not done
Confirm each was a decision with a record, not an oversight.
lecture-python.zh-cn keeps byte-identical copies of all seven datasets plus both builders, unreferenced. Recorded as Track X (D5) — confirm the record exists and that nothing in that repo reads them.
Filenames were not changed during migration (D6), including the redundant 1982/1983 pair. Confirm the deferral is recorded and note that the rename-proposal issue has not been filed — that one is a genuine loose end, not a decision.
lecture-python.notebooks is not listed in any consumers block, on the grounds that it self-heals after a publish. Confirm it regenerated (last push 2026-08-13T09:34:21Z) and carries the new URLs.
The B2′ deletion (#1037) is deliberately not merged-and-forgotten: it will not take effect until the Monday rebuild plus a publish.
Where the reasoning lives
PLAN.md (roadmap, repoint rules, per-track detail) · AGENTS.md (working rules, the four ordering traps, manifest and builder contracts) · migration.yml (per-dataset status and PR trail) · builders/README.md and sources/README.md (coverage and the fetch-vs-sources/ rule) · QuantEcon/workspace-lectures#39 (Track B work plan and the corrections this wave produced) · QuantEcon/workspace-lectures#40 (deletion verification) · QuantEcon/workspace-lectures#41 (the build-cache defect) · and the PR descriptions above, which carry the byte gates and acceptance tests in full.
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.
Everything below was verified during the work, by the same agent that did the work. That is the bias this issue exists to break. Track B of the datasets migration completed today across two waves and two sessions — B1′ (the
olstrio,fp.dta,NEWQDATA.csv) and B2′ (thehansen_singletonpair and their builders) — and both were checked with one set of tools, one shell, one mental model.Bias to test for
The monoculture is
curlfrom a single macOS shell plus bespoke Python extractor scripts written by the agent that made the claims. Nearly every reader-facing conclusion — "the published page is correct", "the repoint changed no figure", "the deleted file is still served" — rests on acontent_numbers()regex extractor written during the session. That extractor produced a wrong answer twice and was patched twice: once it reportedIDENTICAL ✅against files it had never opened, and twice it reported hundreds of spurious differences that were two number streams knocked out of step by the page's own changelog widget. A third undetected flaw in it would silently confirm exactly what the session wanted to hear.A second, quieter monoculture: every builder reproduction ran under the agent's local Python 3.13.9 / pandas 2.3.3, not under any pinned project environment. The
builder_status: committedandintegrity.upstream.status: verifiedclaims both rest on that unpinned run.Validator: confirm reader-facing outcomes without that toolchain. Open the published pages in a browser and read the rendered tables. Fetch from a different machine and network. Re-derive hashes with a different utility. Do not re-run the session's extractor except where a check says to.
What landed
QuantEcon/data-lectures99d2d6d, land B1′), #80 (f363d1b, flip B1′), #81 (2f7d3a3, AGENTS ordering trap), #82 (16ddbbf, land B2′), #83 (da01657, flip B2′)QuantEcon/lecture-python.myste168c7d, repoint B1′), #1035 (d2a5b21, delete B1′), #1036 (a501f03, repoint B2′)QuantEcon/lecture-python.zh-cn3e1289b), #249 (940bd74), #250 (151c402) — translation syncsQuantEcon/lecture-statsPublishes:
lecture-python.mystpublish-2026aug13andpublish-2026aug13b;lecture-python.zh-cnthe same two;lecture-statspublish-2026aug13.Issues: QuantEcon/workspace-lectures#41 opened (build-cache prune); QuantEcon/workspace-lectures#40 body corrected and commented; QuantEcon/workspace-lectures#39 resume block and title rewritten.
1. Reader-facing outcomes
The clearest known incident to confirm: wave B1′'s five files were deleted at 06:31:37Z and a successful publish finished at 09:34:28Z, and the session claims they are STILL SERVED. That is the load-bearing observation behind QuantEcon/workspace-lectures#41.
https://python.quantecon.org/_static/lecture_specific/ols/maketable{1,2,4}.dta,.../mle/fp.dta,.../phillips_drifts_volatilities/NEWQDATA.csvfrom a machine that is not the one that did this work. Record status,content-typeand actual downloaded byte count. Expected today: 200, with 14466 / 11210 / 12024 / 1024085 / 13738 bytes. Confirm a never-existed sibling path 404s in the same run.cache.ymlclean rebuild (Mon 2026-08-17 03:00 UTC) and a publish following it, re-fetch the same five. Expected: 404. Done 2026-08-14, three days early — the rebuild was dispatched manually (run 31757133369) andpublish-2026aug14followed; all five, plus B2′'s six, now 404 with a never-existed control 404ing andols.html200ing in the same pass. The model held, so no new issue. Original wording follows: If any still 200s, the session's model of the trap is incomplete — open a new issue.https://python.quantecon.org/hansen_singleton_1982.htmland.../hansen_singleton_1983.htmlin a browser and confirm the estimate tables and figures render with numbers, not errors or blanks. The data cell ishide-cell, so a failed fetch surfaces only downstream.https://quantecon.github.io/lecture-python.zh-cn/hansen_singleton_1982.htmland1983.html./_notebooks/, and the session never checked that tree. Fetchhttps://quantecon.github.io/lecture-python.zh-cn/_notebooks/hansen_singleton_1982.ipynband1983.ipynband confirm they carrydata-lecturesURLs and no_static/lecture_specific/hansen_singletonreference.lecture-python.mystand search its URI annotations forlecture_specific/hansen_singletonand forlecture-python.myst/raw. Notestringsis invalid on a compressed PDF — decompress the streams. Expected: no old-form references.2. Artifact integrity
Two datasets moved. The claim is byte-identity between what the lectures read before and after.
shasum(e.g.openssl dgst -sha256, or Pythonhashlib) after fetchinghttps://github.com/QuantEcon/data-lectures/raw/main/lectures/hansen_singleton_1982_data.csvand..._1983_data.csv. Expected3e9d4f37c31dbcab26624418f3eeb630d57cc362bd162fa5501cfd992225609b(11,662 B) anddc5c1f8dac4b50abb3613dd297f6c22b4ebde80b9a7f02971e8d986b0c6eff18(26,084 B), matching each manifest'sintegrity.sha256.lecture-python.mystat commita501f03(before #1037 deletes them) and fromlecture-python.zh-cn@main, and confirm all three copies hash identically. A divergence in the zh-cn copy would mean the translation's readers were served different bytes than the English readers.hansen_singleton_1982_data.csvis a bitwise strict subset of the 1983 file — identicaldateindex, andgross_real_return/gross_cons_growthidentical to the last digit. Do it textually (cutthe columns anddiff) rather than with pandas.equals(), which is how the session checked it.git show main:lectures/hansen_singleton_1982_data.csv | head -1must be a CSV header, notversion https://git-lfs....3. The highest-value claim to re-test
builder_status: committedandintegrity.upstream.status: verifiedboth rest on one unpinned local run. More future work depends on this than on anything else today: it is the difference betweencommittedandcommitted-frozen, it licenses editing the builders, and it is the evidence recorded in both manifests.builders/hansen_singleton_1982_data.pyandbuilders/hansen_singleton_1983_data.pyunder the project's pinned environment, not an ambient one, and confirm each reproduces its committed CSV byte for byte. If it does not, the manifests'verifiedstatus is wrong and needs correcting — that is a finding, not a nuisance.verifiedhas a shelf life the manifests do not state.validate()actually rejects. Feed each a deliberately corrupted frame — drop a row, multiply by 100, subtract 1, insert a NaN, rename a column, remove a mid-series month, and for 1983 copygross_real_returnovergross_real_tbill. All seven must raise.F-F_Research_Data_Factors_CSV.zipheader still names a CRSP database vintage and Ibbotson for the pre-2024 T-bill leg, and check whether the data library publishes any terms or usage page linked from its main page. The manifests assert it states no licence at all, based on grepping one fetched HTML page — a linked terms page would refute that and changeredistribution: permittedfrom a reasoned call into an unchecked one.4. Records written
lectures/hansen_singleton_1982_data.csv.ymland..._1983_data.csv.ymlfield-by-field againstmanifest-schema.yml— every required field present, no invented fields,builder_statusandintegrity.upstream.statusfrom the allowed sets.schemaclaim from the bytes rather than trusting it: column names and order,float64dtypes,row_count_floor: 239as an exact count,date_range1959-02-28 to 1978-12-31,known_nulls: {}genuinely empty, and the observed ranges quoted in the column descriptions (returns 0.865–1.159, growth 0.967–1.033, inflation 0.995–1.024, per-capita 2.14e-4–2.66e-4, T-bill 0.984–1.008).consumerslists exactly the repos that read each file today, and that no listed file path is stale.hansen_singletonentries were removed fromscripts/audit_annotations.ymland that no dataset now carries both an annotation and a manifest.builders/README.md's counts are re-derivable from the manifests, not asserted: 21constructed, 14 with a builder (9committed+ 5committed-frozen), 12 distinct builder paths, 7unrecovered, and 13 builder files on disk. These were corrected today after Copilot flagged them; one of them had been wrong since before this wave.5. Tracker consistency
main, run the strict audit and confirm exit 0 with nomigration_inconsistencieswarnings.hansen_singletonentries inmigration.ymltolandedin a scratch checkout and confirm the strict audit fails. A tracker that cannot go red is not evidence when it is green.CATALOG.mdand confirm zero diff. Confirm it reports 33 datasets, 33 read by lectures today, and that both new rows showconstructed,✅ verified,✅ committed, and a non-empty Used by column.migration.ymlrecords all seven Track B datasets atstatus: repointedwithcutover: null, and that everyrepoints[].prnames a PR that is genuinely merged.6. Known blind spots
The session documented three. Verify each blind spot is real, and re-derive the counts — and note that the sweep producing a count often shared the blind spot it documents.
lecture-python.zh-cnis excluded frombuild_audit.py'sSCAN_REPOS, so no automated check ever sees its reads. Confirm the exclusion, then confirm by hand that both zh-cn lectures readdata-lecturesonmainand that this is recorded in the manifests'consumers— because if it is not recorded there, it is recorded nowhere.python.quantecon.org/_static/lecture_specific/mle/fp.dta, so the assumption that consumers are in-org is known to be false for this family.gh search codecannot match a bare URL and cannot see archived repos. Confirm this independently, and confirm the session's mitigation (Trees-API enumeration) actually covers archived repos.wc -lagainstgrep -c ''. Re-run the sweep with a method that cannot truncate, and confirm the total is 88, not 87.7. Decisions settled today
committed, notcommitted-frozen. Re-test per section 3.ci.yml,publish.ymlandcache.ymlonorigin/main— the session read local clones, which can lag. Confirmlecture-wasmis the only immune one, and confirmlecture-dp's build-cache key is built only from environment-file hashes so content changes never invalidate it.jb clean --htmlremoves only_build/htmland preserves.jupyter_cache. Verify against the pinned jupyter-book version actually used by CI, and confirm that version is what CI installs.us_adult_heights.csvandjapan_population_by_age.xlsxare present in thepublish-2026aug06andpublish-2026aug07release tarballs forlecture-python-introand absent frompublish-2026aug11.lecture-statsis not a B2′ consumer. Re-check againstorigin/main, with a positive control proving the search pattern matches when present.8. Deliberately not done
Confirm each was a decision with a record, not an oversight.
lecture-python.zh-cnkeeps byte-identical copies of all seven datasets plus both builders, unreferenced. Recorded as Track X (D5) — confirm the record exists and that nothing in that repo reads them.lecture-python.notebooksis not listed in anyconsumersblock, on the grounds that it self-heals after a publish. Confirm it regenerated (last push 2026-08-13T09:34:21Z) and carries the new URLs.Where the reasoning lives
PLAN.md(roadmap, repoint rules, per-track detail) ·AGENTS.md(working rules, the four ordering traps, manifest and builder contracts) ·migration.yml(per-dataset status and PR trail) ·builders/README.mdandsources/README.md(coverage and the fetch-vs-sources/rule) · QuantEcon/workspace-lectures#39 (Track B work plan and the corrections this wave produced) · QuantEcon/workspace-lectures#40 (deletion verification) · QuantEcon/workspace-lectures#41 (the build-cache defect) · and the PR descriptions above, which carry the byte gates and acceptance tests in full.