Skip to content

docs-jenner: skip notebook re-execution (~40 min → ~1 min), add opt-in -execute variant - #237

Closed
jonathanhhb wants to merge 4 commits into
mainfrom
perf/jenner-doc-build-skip-notebook-exec
Closed

docs-jenner: skip notebook re-execution (~40 min → ~1 min), add opt-in -execute variant#237
jonathanhhb wants to merge 4 commits into
mainfrom
perf/jenner-doc-build-skip-notebook-exec

Conversation

@jonathanhhb

Copy link
Copy Markdown
Collaborator

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

  • `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.
  • 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.
  • `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 useful and it's now opt-in via `docs-jenner-execute` rather than baked into every build. Follow-ups worth considering separately:

  • 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, matching or slightly beating the fresh-exec baseline (19/21 with 5-7 a1) on the same stripped-server config.

…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).
@jonathanhhb
jonathanhhb requested review from YeChen-IDM and Copilot and removed request for YeChen-IDM July 7, 2026 23:03

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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-jenner to skip docs/execute_notebooks.py and concatenate directly from docs/ notebook outputs.
  • Add docs-jenner-execute to preserve the full historical pipeline for validation/release-time builds.
  • Add an execute_notebooks boolean 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.

Comment thread Makefile Outdated
Comment thread Makefile
Comment thread Makefile
jonathanhhb and others added 3 commits July 7, 2026 16:05
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>
jonathanhhb added a commit that referenced this pull request Jul 9, 2026
…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>
@jonathanhhb

Copy link
Copy Markdown
Collaborator Author

Closing as obsolete

The 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:

  • Notebook execution moved into its own workflow (execute-notebooks.yml), source-hash-cached, publishes an executed_nbs artifact (verified working end-to-end at run 29056479852).
  • build-combined-doc.yml was completely rewritten to consume that artifact instead of re-executing, wall clock down to ~3-5 min on cache hit / artifact download.
  • New make docs-jenner-artifact Makefile target for the CI-side "assume $(EXEC_DIR) is pre-populated" path (already on main via ci: split notebook execution into its own workflow with cached artifact (closes #186) #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. docs-jenner-fast) with an explicit "may render stale figures" doc comment would be the right shape.

Branch will be deleted — the diff is captured in the commit history if we ever want to salvage.

Related PRs:

@jonathanhhb jonathanhhb closed this Jul 9, 2026
@jonathanhhb
jonathanhhb deleted the perf/jenner-doc-build-skip-notebook-exec branch July 9, 2026 23:57
jonathanhhb added a commit that referenced this pull request Jul 10, 2026
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>
jonathanhhb pushed a commit that referenced this pull request Jul 10, 2026
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>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants