Summary
Every PR runs a full, cold ~37-minute build because the global Actions cache is empty — there is never a cache hit. The restore wiring in ci.yml is correct; the problem is upstream of it: the cache is never produced. There are two independent reasons, plus one minor config nit.
Evidence
| Check |
Result |
gh cache list |
empty (no caches exist) |
| Recent PR CI runs |
37–39 min each, log shows ⚠️ No cache found |
Build Cache (cache.yml) runs |
every run failed, weekly from 2026-03-23 through 2026-05-25 |
| Build Cache runs since 2026-06-01 |
none |
| Build Cache workflow state |
disabled_inactivity |
Cause 1 — the Build Cache workflow is currently disabled
The Build Cache workflow state is disabled_inactivity (GitHub auto-disabled the schedule) and it has not run since 2026-05-25. While disabled, the weekly job never runs, so the cache is never (re)built regardless of anything else. It needs to be re-enabled in the Actions tab (or via gh workflow enable), then kicked once via workflow_dispatch to seed a cache.
Cause 2 — even when it ran, the build failed (so no cache was saved)
build-jupyter-cache only saves the cache when all builds pass. Every run failed on lectures/un_insure.md with a CellExecutionError, so all-passed=false and the save step was skipped — leaving the cache empty.
This is not a bug in our lecture: un_insure.md builds successfully in our PR CI on a cold build (run 27321596555, executed in 8.9s) and renders correctly on the upstream live site (https://python-advanced.quantecon.org/un_insure.html). The root cause is the unpinned numpy/scipy in the quantecon-build:latest container, already tracked in QuantEcon/actions#28. Because :latest is a moving tag, the same workflow passes or fails depending on when the image was last rebuilt — which is why our PR builds and cache builds disagree.
The reason this rotted unnoticed for months (no alert issue, no debuggable traceback in the cache path) is a set of tooling gaps tracked in QuantEcon/actions#83.
Cause 3 (minor) — builder vs builders typo in cache.yml
cache.yml line 28 passes builder: 'html', but the build-jupyter-cache input is builders (plural). The value is silently ignored; it happens to be harmless today because the default is already html, but it should be corrected to avoid a future surprise.
Suggested actions
Summary
Every PR runs a full, cold ~37-minute build because the global Actions cache is empty — there is never a cache hit. The restore wiring in ci.yml is correct; the problem is upstream of it: the cache is never produced. There are two independent reasons, plus one minor config nit.
Evidence
gh cache list⚠️ No cache foundcache.yml) runsdisabled_inactivityCause 1 — the Build Cache workflow is currently disabled
The
Build Cacheworkflow state isdisabled_inactivity(GitHub auto-disabled the schedule) and it has not run since 2026-05-25. While disabled, the weekly job never runs, so the cache is never (re)built regardless of anything else. It needs to be re-enabled in the Actions tab (or viagh workflow enable), then kicked once viaworkflow_dispatchto seed a cache.Cause 2 — even when it ran, the build failed (so no cache was saved)
build-jupyter-cacheonly saves the cache when all builds pass. Every run failed onlectures/un_insure.mdwith aCellExecutionError, soall-passed=falseand the save step was skipped — leaving the cache empty.This is not a bug in our lecture:
un_insure.mdbuilds successfully in our PR CI on a cold build (run 27321596555, executed in 8.9s) and renders correctly on the upstream live site (https://python-advanced.quantecon.org/un_insure.html). The root cause is the unpinnednumpy/scipyin thequantecon-build:latestcontainer, already tracked in QuantEcon/actions#28. Because:latestis a moving tag, the same workflow passes or fails depending on when the image was last rebuilt — which is why our PR builds and cache builds disagree.The reason this rotted unnoticed for months (no alert issue, no debuggable traceback in the cache path) is a set of tooling gaps tracked in QuantEcon/actions#83.
Cause 3 (minor) —
buildervsbuilderstypo in cache.ymlcache.yml line 28 passes
builder: 'html', but thebuild-jupyter-cacheinput isbuilders(plural). The value is silently ignored; it happens to be harmless today because the default is alreadyhtml, but it should be corrected to avoid a future surprise.Suggested actions
Build Cacheworkflow (currentlydisabled_inactivity) and trigger oneworkflow_dispatchrun to seed the cache.un_insure.mdfsolvefix.builder:→builders:typo incache.yml.