Handover note for resuming the action-harness work. PR #114 (stage 1 of #100) squash-merged to main as 99b81ed on 2026-07-30 — this issue collects everything that came out of that review and did not belong in the PR itself.
What landed
.github/workflows/test-actions.yml — a 14-job harness that exercises the composite actions in this repo via uses: ./<action> local paths, so it tests the code on the PR rather than a released ref. Plus the committed fixture .github/fixtures/mini-lectures/ (a two-page jupyter-book that executes a real code cell, so builds populate a genuine _build/.jupyter_cache), and two consumer-facing fixes the harness caught on its first run: the setup-environment conda cache was save-only on hosted runners (rooted at the root-owned ${CONDA}/envs), and build-lectures failed the step on a successful build because the exit builtin in a bash -l step triggers ~/.bash_logout, whose clear_console failure overrides the passed status. PLAN item 9 is closed. CONTRIBUTING gained release step 5 (drop obsolete @v0 workarounds when the floating tag moves).
#100 stays open for stages 2–4 (the QuantEcon/test-lectures canary repo, preview/publish coverage, tag-push trigger) — those need a separate repo plus Netlify/Cloudflare secrets.
Follow-ups, in priority order
| # |
Item |
Blocked on |
Notes |
| 1 |
Drop the rm -f ~/.bash_logout step from the bjc-smoke job |
The next release moving the floating v0 tag |
It exists only because build-jupyter-cache's internal build-lectures@v0 still carries the pre-fix exit pattern, and GitHub forbids expressions in uses: so that copy cannot pick the fix up until the tag moves. CONTRIBUTING step 5 is the tripwire; this is the one knowingly-temporary thing in the tree |
| 2 |
Delete the dead tmp-verify-restore-cache.yml entry from the Actions sidebar |
— |
Leftover registry entry from the throwaway harness that verified #104. The file exists on no branch, so it can never run and a commit cannot remove it — needs a click in the Actions UI |
| 3 |
Tighten the negative build test's failure attribution |
— |
build-fail-guard asserts the step outcome is failure, which would also be satisfied by a build that failed for an unrelated -W warning. Asserting harness/_build/html/reports/broken.err.log exists ties the red specifically to the execution error |
| 4 |
Document the -W dependence in the build-lectures README |
— |
See the note below; it is a real consumer-facing property, currently recorded nowhere in the docs |
| 5 |
Companion workflow with negated paths:, if branch protection is ever enabled on main |
Someone enabling protection |
See the required-check note below |
Two properties worth knowing before touching this again
A failing build only fails because of -W. The build-fail-guard job builds a page whose code cell raises. jupyter-book reports the CellExecutionError as a warning and keeps going, so the nonzero exit comes from -W in the extra-args default (-W --keep-going), not from the exception itself. The consequence for lecture repos: overriding extra-args without -W means a raised cell yields a published site plus an error-report page and exit 0 — a green CI over a broken lecture. Removing -W from the default would flip this harness job red, intentionally.
harness-summary as a required check would hang some PRs. The workflow has paths: filters. For pull_request events GitHub evaluates those against the whole PR diff, not the head commit, so any PR touching a covered path runs the harness — a docs-only commit pushed onto a PR that already touches a covered path is fine. But a PR whose entire diff touches none of the covered paths (docs-only, or publish-gh-pages/** only) never triggers the workflow, so a required Action harness: all checks would sit pending forever and block the merge. main was unprotected as of this merge, so this is latent. The fix is a companion workflow with the negated paths that emits a passing job of the same name.
Watch items, no action needed
The fixture pins jupyter-book==1.0.4post1 but leaves ipykernel and the python=3.13 resolution floating, so an upstream change can redden the harness on a PR that touched nothing related — arguably correct for a canary, but expect the occasional unrelated failure, and note that pinning policy properly belongs to the stage-2 canary. Separately, per-run cache salting (run_id-run_attempt, which is what makes the miss assertions trustworthy) means every run writes caches that are never reused again, including roughly two conda envs per run; that churns the repo's 10 GB cache budget and will LRU-evict the container-test caches. Both are by design.
Verification state at merge
All 14 jobs green on the PR head (9b379f5), plus an independent workflow_dispatch run on the previous commit with separately seeded caches. The post-merge push: main run is https://github.com/QuantEcon/actions/actions/runs/30524512574 — first run with f31f980 in the tree, and the run whose saved caches future PR runs will see.
Related: #100 (parent), #104 (the DOA save-mode bug that motivated the harness), #108 (container fixture cannot fail meaningfully), #109 (template drift — the stage-2 canary should consume templates verbatim to cover it).
Handover note for resuming the action-harness work. PR #114 (stage 1 of #100) squash-merged to
mainas 99b81ed on 2026-07-30 — this issue collects everything that came out of that review and did not belong in the PR itself.What landed
.github/workflows/test-actions.yml— a 14-job harness that exercises the composite actions in this repo viauses: ./<action>local paths, so it tests the code on the PR rather than a released ref. Plus the committed fixture.github/fixtures/mini-lectures/(a two-page jupyter-book that executes a real code cell, so builds populate a genuine_build/.jupyter_cache), and two consumer-facing fixes the harness caught on its first run: thesetup-environmentconda cache was save-only on hosted runners (rooted at the root-owned${CONDA}/envs), andbuild-lecturesfailed the step on a successful build because theexitbuiltin in abash -lstep triggers~/.bash_logout, whoseclear_consolefailure overrides the passed status. PLAN item 9 is closed. CONTRIBUTING gained release step 5 (drop obsolete@v0workarounds when the floating tag moves).#100 stays open for stages 2–4 (the
QuantEcon/test-lecturescanary repo, preview/publish coverage, tag-push trigger) — those need a separate repo plus Netlify/Cloudflare secrets.Follow-ups, in priority order
rm -f ~/.bash_logoutstep from thebjc-smokejobv0tagbuild-jupyter-cache's internalbuild-lectures@v0still carries the pre-fixexitpattern, and GitHub forbids expressions inuses:so that copy cannot pick the fix up until the tag moves. CONTRIBUTING step 5 is the tripwire; this is the one knowingly-temporary thing in the treetmp-verify-restore-cache.ymlentry from the Actions sidebarbuild-fail-guardasserts the step outcome isfailure, which would also be satisfied by a build that failed for an unrelated-Wwarning. Assertingharness/_build/html/reports/broken.err.logexists ties the red specifically to the execution error-Wdependence in thebuild-lecturesREADMEpaths:, if branch protection is ever enabled onmainTwo properties worth knowing before touching this again
A failing build only fails because of
-W. Thebuild-fail-guardjob builds a page whose code cell raises. jupyter-book reports theCellExecutionErroras a warning and keeps going, so the nonzero exit comes from-Win theextra-argsdefault (-W --keep-going), not from the exception itself. The consequence for lecture repos: overridingextra-argswithout-Wmeans a raised cell yields a published site plus an error-report page and exit 0 — a green CI over a broken lecture. Removing-Wfrom the default would flip this harness job red, intentionally.harness-summaryas a required check would hang some PRs. The workflow haspaths:filters. Forpull_requestevents GitHub evaluates those against the whole PR diff, not the head commit, so any PR touching a covered path runs the harness — a docs-only commit pushed onto a PR that already touches a covered path is fine. But a PR whose entire diff touches none of the covered paths (docs-only, orpublish-gh-pages/**only) never triggers the workflow, so a requiredAction harness: all checkswould sit pending forever and block the merge.mainwas unprotected as of this merge, so this is latent. The fix is a companion workflow with the negated paths that emits a passing job of the same name.Watch items, no action needed
The fixture pins
jupyter-book==1.0.4post1but leavesipykerneland thepython=3.13resolution floating, so an upstream change can redden the harness on a PR that touched nothing related — arguably correct for a canary, but expect the occasional unrelated failure, and note that pinning policy properly belongs to the stage-2 canary. Separately, per-run cache salting (run_id-run_attempt, which is what makes the miss assertions trustworthy) means every run writes caches that are never reused again, including roughly two conda envs per run; that churns the repo's 10 GB cache budget and will LRU-evict the container-test caches. Both are by design.Verification state at merge
All 14 jobs green on the PR head (9b379f5), plus an independent
workflow_dispatchrun on the previous commit with separately seeded caches. The post-mergepush: mainrun is https://github.com/QuantEcon/actions/actions/runs/30524512574 — first run withf31f980in the tree, and the run whose saved caches future PR runs will see.Related: #100 (parent), #104 (the DOA save-mode bug that motivated the harness), #108 (container fixture cannot fail meaningfully), #109 (template drift — the stage-2 canary should consume templates verbatim to cover it).