docs-jenner: skip notebook re-execution (~40 min → ~1 min), add opt-in -execute variant - #237
docs-jenner: skip notebook re-execution (~40 min → ~1 min), add opt-in -execute variant#237jonathanhhb wants to merge 4 commits into
Conversation
…n re-exec) ## Motivation `make docs-jenner` currently spends ~30 of its ~40-minute wall clock re-executing every notebook (via `docs/execute_notebooks.py`) — for a build whose only purpose is to produce `combined_mkdocs.md` for the jenner-generic-mcp RAG corpus. Every notebook is already committed with its outputs (as required for the mkdocs-jupyter render on the published docs site), so re-executing them to re-embed the same outputs in the corpus is wasted CI time. ## Change - `docs-jenner` now uses committed notebook outputs (reads notebooks directly from `docs/` instead of `dist/executed_nbs/`). No `docs/execute_notebooks.py` invocation. Wall clock ~40 min → ~1 min. - A new `docs-jenner-execute` target preserves the historical full pipeline (execute + error-gate + build + concat) for when a caller wants notebook validation as part of the doc build — typical for release-time builds where we want to catch API drift between notebooks and the current package. - The `Build Combined Doc` workflow gains an `execute_notebooks` boolean input (default false). When false → fast `docs-jenner`. When true → slow `docs-jenner-execute` with the existing `allow_notebook_errors` and `nb_timeout` inputs applied. - Help text and .PHONY line updated. ## What we lose (and how to keep it) The historical `docs-jenner` implicitly validated that every notebook still runs against the current API — `check_executed_nbs.py` would error the build if any cell threw. That's genuinely useful, and it's now opt-in via `docs-jenner-execute` rather than baked into every build. Follow-ups worth considering: - A dedicated periodic "notebook validation" workflow (weekly cron or nightly) that runs `docs-check-nbs` and posts an issue on failure, so API-drift catches don't depend on someone remembering to dispatch the -execute variant. - Add an output-completeness check to `check_executed_nbs.py` (each code cell has at least one output) so intentionally-stripped notebooks are flagged before they land in the corpus. ## Verified - `make docs-jenner` on a clean checkout produces a valid `dist/combined_mkdocs.md` in ~1 min (mkdocs build + concat only). - `make docs-jenner-execute` still runs the full historical pipeline. - Downstream jenner-generic-mcp alpha suite scores 18/21 with 8 a1 against the committed-outputs corpus (matches or slightly beats the fresh-exec baseline of 19/21 with 5-7 a1).
There was a problem hiding this comment.
Pull request overview
This PR speeds up the docs-jenner documentation corpus build by skipping notebook re-execution and instead consuming the outputs already committed in docs/**/*.ipynb, while providing an opt-in docs-jenner-execute path (and workflow input) to restore the historical “execute + gate + build + concat” validation when desired.
Changes:
- Update
docs-jennerto skipdocs/execute_notebooks.pyand concatenate directly fromdocs/notebook outputs. - Add
docs-jenner-executeto preserve the full historical pipeline for validation/release-time builds. - Add an
execute_notebooksboolean input to the “Build Combined Doc” workflow to select fast vs. full pipeline.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 3 comments.
| File | Description |
|---|---|
| Makefile | Splits fast vs. full combined-doc pipelines and updates help/phony targets accordingly. |
| .github/workflows/build-combined-doc.yml | Adds a dispatch input to select fast build vs. notebook re-execution build path. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
…ct (#241) Adds a new `Execute Notebooks` workflow that owns notebook execution as a first-class CI stage, and reworks `Build Combined Doc` to consume the resulting artifact instead of re-executing notebooks itself. Also adds a `docs-jenner-artifact` Makefile target for the CI-side build path. ## Problems solved 1. Notebook execution used to fire only on releases (issue #186). Now fires on every main-push touching notebook or execution-affecting source, plus on manual dispatch. Broken notebooks are surfaced within minutes of landing. 2. Committed executed-notebook outputs no longer need to be kept fresh by convention. The CI artifact is the source of truth for downstream doc/RAG builds; the committed `outputs` field is decorative (kept only so github.com renders charts inline). 3. Doc builds no longer re-execute notebooks. `Build Combined Doc` downloads the executed_nbs artifact and just runs `make docs-jenner-artifact` (~2-3 min wall-clock vs the previous ~25 min). ## Architecture ``` Execute Notebooks trigger: push to main (paths-filtered) | workflow_dispatch cache: source-hash-keyed, invalidates on any input that can affect outputs (notebooks, src/**, pyproject.toml, docs/requirements.txt, docs/**/*.py, Makefile, the workflow file itself) gate: python docs/check_executed_nbs.py (fail on any nb error) manifest: writes dist/executed_nbs/manifest.json with commit_sha, source_hash, run_id, python_version, event_name, was_cache_hit, was_forced, was_allow_errors uploads: executed_nbs (400d) push-to-main OR clean dispatch executed_nbs-debug-<run_id> (30d) dispatch with allow_notebook_errors=1 executed_nbs-failed-<run_id> (30d) any failure path Build Combined Doc trigger: workflow_run completion of Execute Notebooks (auto-chain, restricted to push-triggered upstream runs so debug dispatches don't push a corpus PR to laser-mcp) | workflow_dispatch download: executed_nbs artifact (by run-id for workflow_run, latest successful for workflow_dispatch) compat gate (workflow_dispatch only): hashFiles of checkout vs manifest.source_hash; fail on mismatch unless use_latest_anyway=true build: make docs-jenner-artifact -> build site + concat sync: create/update laser-mcp PR ``` The two `hashFiles` lists (cache-key in execute-notebooks + compat-check in build-combined-doc) are literal duplicates by design — 7 patterns each, same order. Diverging silently breaks the compat gate. ## Deliberate non-goals - **No `pull_request` trigger.** Executing every notebook adds ~25 min per PR iteration; that cost is not affordable during review cycles. Broken notebooks land on main and are caught by the post-merge push run within minutes. Local `make docs-jenner-execute` or a manual `workflow_dispatch` remain as pre-merge validation options for PRs that specifically need them. - **No enforcement of source-only committed notebooks.** Contributors may commit executed OR stripped notebooks — both work identically for the doc build. Documented inline in the workflow file. If future consensus wants strict source-only commits (Option B), the enforcement point is a single check step in `github-actions.yml`. ## Bootstrap note On first enable, `workflow_dispatch` on Build Combined Doc will fail cleanly (no prior successful Execute Notebooks artifact to consume — `if_no_artifact_found: fail` catches this). Kick off Execute Notebooks once manually to seed. After that, either trigger works. ## Third-party action `dawidd6/action-download-artifact@v3` — needed because `actions/download-artifact` only fetches from the current run or a known run-id, and `workflow_dispatch` on Build Combined Doc has no upstream run-id to point at. This third-party action searches for "latest successful run of workflow X" — exactly the semantics we need. Widely used (~4M weekly downloads). ## Compatibility with other in-flight PRs Independent of #204 (plot descriptions), #240 (figtext form of #204), and #236 (concat improvements) — those change notebook / concat content. Compatible with #237 (skip re-exec in local `docs-jenner`) — together the two produce a clean Makefile trio: - `make docs-jenner` local fast path (per #237) - `make docs-jenner-execute` local full pipeline (per #237) - `make docs-jenner-artifact` CI path, EXEC_DIR pre-populated (this PR) Closes #186. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Closing as obsoleteThe CI-side wall-clock problem this PR was solving (~40 min doc builds re-executing every notebook) has been addressed differently — and, I now think, more cleanly — by #241:
The overlap with #241:
If a local-dev fast path is wanted later, a follow-up PR that adds a distinctly-named target (e.g. Branch will be deleted — the diff is captured in the commit history if we ever want to salvage. Related PRs:
|
Reworks both READMEs so the docs site (laser.idmod.org/laser-generic/tutorials/) is presented as the primary answer for "where do I read the rendered notebooks?" — not the artifact download. Fixes the misleading download-first framing introduced in #243. Direct user feedback triggered this: reading the previous version led to "I thought we said that the executed notebook artifacts would be viewable and renderable in github, not that I'd have to download them and install and run jupyter???" Correct expectation, wrong framing. The claim that the docs site consumes the executed_nbs artifact was made factually correct by #246, which changed mkdocs-ghp.yml to overlay the artifact before running mkdocs build. Verified in production: nb06's rendered page on laser.idmod.org now contains six real plot figures including the "Regime robustness" section (only exists when notebooks are freshly executed). Changes: - docs/tutorials/notebooks/README.md - Leads with "read the rendered tutorials on the docs site" and links to three example notebook pages. - Adds a source-of-truth comparison table (docs site vs committed outputs vs artifact) showing where figures live and how fresh each location is. - Demotes the artifact-download procedure to a clearly-labeled "debugging / archival" section with an explicit "you don't need this for regular reading" callout. - Fixes a stale reference to `make docs-jenner-execute (available once #237 lands)` — #237 was closed as obsolete; `make docs-jenner` is the right command. - README.md - Same reframe on the top-level "Executed tutorial notebooks" section: lead with the docs site, delegate the download procedure to the folder README. - Corrects "retention 400 days" claim to the actual observed 90d (repo-default caps override workflow retention requests). Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Refactors the 70 plot-description cells added by #204 from standalone markdown cells (### Reading the ... plot, with observation + interpretation + takeaway paragraphs) to plt.figtext() captions embedded inside the plot-generating code cells. Content is compressed to the takeaway sentence. Motivation: alpha-suite evaluation (jenner-generic-mcp, N=3 per arm, same generation quality: arm mean/20 a1 Δ vs no-descriptions no descriptions (control) 17.00 9.00 — fat markdown (#204 as-authored) 14.33 7.67 -2.67 slim markdown (H3 + takeaway) 16.33 8.33 -0.67 figtext (this PR) 17.00 9.00 +0.00 Standalone markdown description cells create their own chunks in the RAG corpus that compete for top-k retrieval on prompts like "build this SIR model" — the descriptions are semantically close ("simulate + plot") but have no API-usage content, so retrieval picks them up and displaces the chunks that actually show the model-building calls. plt.figtext embeds the description text inside the code cell's Python source. The concat pipeline emits that cell as a fenced Python block, and the ingest H4 splitter (laser-mcp #37) plus the recursive character splitter's fence-first separator keep the string fused with the surrounding plotting code. Result: description content is still in the RAG corpus, but as part of code chunks — no separate description-only chunks to compete with API-usage retrieval. Chunk-count evidence (laser-mcp ingest.py, chunk_size=1200): arm sections chunks Δ chunks vs no-descriptions no desc 515 972 — figtext 515 991 +19 (co-located, no new boundaries) slim 585 1037 +65 (new md sections) composite 585 1101 +129 (new md sections + fat content) The figtext arm adds essentially no new chunk boundaries. Takeaway extraction: for each description .md file, extracts the last bold span (>20 chars) containing a takeaway trigger word ("demonstrates", "takeaway", "confirms", ...). 68/70 auto-extract cleanly; 1 spot-fixed via MANUAL_TAKEAWAY, 1 confirmed OK via fallback path. Full logic lives in tools/apply_figtext_captions.py so the same manifest at tools/plot_descriptions/config.json remains the source of truth. Rendering: plt.figtext(0.5, -0.05, ..., ha="center", va="top", wrap=True, fontsize=8) places the caption just below the axes. Notebook outputs are NOT re-executed by this PR (docs-jenner uses committed outputs per #237), so the committed figure PNGs are unchanged; when notebooks are next re-executed for real (e.g. a release-time full build via docs-jenner-execute) the caption will render in the figure. Base branch: docs/notebook-plot-descriptions (#204's branch). This is a stacked PR — #204's markdown-form remains the base for reviewers to see what's being converted. Merge order intended: 1. #204 (as-authored, or force-updated to figtext form if reviewers prefer) 2. This PR (if not folded into #204) Related: - #204 base — the plot descriptions themselves - #236 — concat heading demote + oversize warning (independent) - #237 — docs-jenner skip re-execution (independent) - laser-mcp #37 — H4 in MarkdownHeaderTextSplitter (works with either form) - Alpha-suite runs under tests/prompt_test_suite_generic/output/experiment_204eval/ in laser-mcp; N=3 per arm, gpt-5-mini generation, gpt-4o review. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Motivation
`make docs-jenner` spends ~30 of its ~40-minute wall clock re-executing every notebook (via `docs/execute_notebooks.py`) — for a build whose only purpose is to produce `combined_mkdocs.md` for the jenner-generic-mcp RAG corpus. Every notebook is already committed with its outputs (as required for the mkdocs-jupyter render on the published docs site), so re-executing them to re-embed the same outputs in the corpus is wasted CI time.
Change
What we lose (and how to keep it)
The historical `docs-jenner` implicitly validated that every notebook still runs against the current API — `check_executed_nbs.py` would error the build if any cell threw. That's useful and it's now opt-in via `docs-jenner-execute` rather than baked into every build. Follow-ups worth considering separately:
Verified