This plan tracks bringing the MyST theme (this repo, quantecon-theme.mystmd —
formerly quantecon-theme-src, and no longer bundled to the now-archived
QuantEcon/quantecon-theme —
releases ship as GitHub Release zips, see Phase 0) to
feature parity with the Sphinx / Jupyter-Book<2 theme
quantecon-book-theme ahead of
migrating the QuantEcon lectures to MyST (Jupyter Book ≥ 2).
The two themes run in fundamentally different environments:
quantecon-book-theme (Sphinx) |
quantecon-theme.mystmd (MyST) |
|
|---|---|---|
| Runs | At build time, inside the lecture repo | As a runtime Remix server consuming pre-built content JSON from the MyST content CDN |
| Has the git repo? | Yes — can shell out to git |
No — only sees per-page mdast + frontmatter JSON |
| Customised via | Jinja templates + a Sphinx extension (__init__.py) |
React/TSX components + myst.yml config |
Consequence: any feature that derives from the source repo at build time (git
history, last-modified dates, computed launch paths) cannot be computed by the
theme. It must be injected into the page frontmatter/data upstream — either by:
- (A) a MyST plugin / transform that runs during
myst buildin the lecture repo and writes the data into each page's frontmatter, or - (B) built-in
mystmdsupport (where it already exists — e.g. frontmatterdate), or - (C) a CI step (GitHub Action) that pre-computes the data and feeds it in.
The theme component's job is then only to render page.frontmatter.<field>. This
split is called out per phase below. Several phases therefore have an "upstream"
deliverable in addition to the theme change.
A second cross-cutting consideration: this theme tracks the upstream
jupyter-book/myst-theme book theme.
Before building anything custom, check whether the feature already exists upstream
(or could be contributed upstream rather than forked into QuantEcon-only code).
Derived from quantecon-book-theme v0.20.3 (see its README.md, docs/user/*, and
src/quantecon_book_theme/).
| Feature | Book-theme | MyST theme | Phase |
|---|---|---|---|
| Git history in lecture headers (last-modified + changelog dropdown) | ✅ | ✅ | 1 |
| Launch parity — Thebe (live compute) in addition to Colab / private hub (BinderHub dropped, #26) | ✅ | ✅ | 2 |
| Configurable code highlighting (custom QE tokens vs Pygments styles) | ✅ | ❌ | 3 |
Text colour schemes (seoul256 / gruvbox / none + custom) |
✅ | ❌ | 3 |
Language switcher (multilingual) + hreflang SEO tags |
✅ | ❌ | 4 |
RTL support (dir="rtl") |
✅ | ❌ | 5 |
| Collapsible stderr warnings in notebook cells | ✅ | ❓ verify | 6 |
| Full OpenGraph / Twitter card meta tags | ✅ | 6 | |
| Already at parity: dark mode, font scaling, fullscreen, search, "On this page" TOC + back-to-top, contents sidebar, downloads (PDF/notebook), Colab + private JupyterHub launch, edit-on-GitHub, author header, content-driven footer, responsive/mobile | ✅ | ✅ | — |
Shipped state (2026-08-20). Phase 0 completed across v2.1.0–v2.2.0; Phase 1 and the Thebe half of Phase 2 shipped in v2.3.0, and Phase 2's launch-config half shipped in v2.2.0. Phase 3 is next.
Status: complete (reviewed 2026-08-20) — pipeline, rename, archive and the only live consumer all landed. The two unchecked boxes below are deferred by design: the flagship lecture repos repoint at their own MyST cutover.
Why first: the deployed bundle is stuck at v1.1.1 (June 2025) and predates the
entire @myst-theme 1.x upgrade. Parity work is meaningless until what's in main is
shipped and the release loop is trustworthy. This also uses the pre-cutover window —
while the theme has just one consumer (lecture-wasm; see the migration checklist below) —
to fix the repo architecture cheaply.
Decision — collapse to a single repo that distributes GitHub Release zips. Today the
theme is a two-repo split: source here is built by make deploy and pushed to a separate
build repo (QuantEcon/quantecon-theme), whose main-branch zip lectures download. That
split is inherited from upstream (jupyter-book/myst-theme → myst-templates/book-theme),
where it exists because upstream is a monorepo producing many products (npm packages +
the book & article themes) feeding a named public registry — neither applies to us
(one theme, consumed by direct URL). Upstream is itself moving off it: their
theme-assets.yml ships built theme zips as GitHub Release assets "rather than pushing
to the myst-templates/*-theme repos … a step toward using GitHub releases instead"
(jupyter-book/myst-enhancement-proposals#34). So a single repo + release zips tracks where
upstream is heading.
Target architecture:
- One repo, renamed
quantecon-theme.mystmd(the.mystmdsuffix marks it asmystmdtooling/renderer, distinct from content. Note: we deliberately do not reuse the lectures'.mystsuffix — that was a transitional RST→MyST marker now being retired org-wide.) - Each release is a
vX.Y.Zgit tag + GitHub Release with a built theme zip attached. - Lectures set
site.templateto a direct GitHub zip URL for a pinned release. - Deliberate simplification (the caveat): because lectures consume a direct URL, we
do not need the MyST template registry or any
myst-clitemplate-resolution support — those are exactly the parts of the upstream setup we skip. Build a zip, attach it to a release, point the URL at it.
Repo consolidation & rename:
- Rename
quantecon-theme-src→quantecon-theme.mystmd(GitHub 301-redirects the old URLs; update the localoriginremote). (Done 2026-06-11.) - Once releases are live, archive the old build repo
QuantEcon/quantecon-themewith a README note pointing to the new repo + release assets; do not reuse the name. (Done 2026-06-11, afterlecture-wasmwas repointed in lecture-wasm#48.) - Retire the
make deploy/build-theme/deploy-themeMakefile targets (replaced by the release workflow); keep a localmake build-zipfor testing the artifact. (Done in #81: deploy targets removed;build-themeretained for the test harness but now assembles locally instead of cloning the archived repo;build-zipadded.)
Release pipeline (adapt upstream theme-assets.yml):
- Add a workflow that, on a
vX.Y.Ztag, builds (npm run prod:build), assembles the bundle (build/ public/ template.yml server.js package.jsonwithtemplate→name andVERSIONsubstituted, pluspackage-lock.json, excludingnode_modules), zips it, and attaches it to the GitHub Release for that tag. Upstream'stheme-assets.ymlis a near-verbatim reference — drop itsfor THEME in book articleloop (one theme). Done in #77 —.github/workflows/release.yml: tag-triggered, guards tag ↔package.jsonversion, requires (and uses as release notes) the version'sCHANGELOG.mdsection. - Verify
myst startresolvessite.template: <release-asset-url>once (a release zip is just an HTTPS zip — the same mechanism today'sarchive/refs/heads/main.zipuses — so low risk). Verified 2026-06-11 against the v2.1.0 release URL:myst startdownloaded, built, and served the visual fixture with the released theme.
Versioning & changelog: (remaining work tracked in #71)
- Make
vX.Y.Ztags the release trigger (trigger wired up in #77); tag existing releases retroactively at their source commits (recorded in the old build repo as🚀 vX.Y.Z from <sha>) so theCHANGELOG.mdfooter compare/release links resolve. (Retro-tagged 2026-06-11: v1.0.0–v1.1.1 + v2.0.0 pushed at their verified source commits — each commit'spackage.jsonmatches its tag; v1.1.0 uses the later of its two deploys — withrelease.ymltemporarily disabled so historical tags triggered no runs. Compare links verified resolving.) - Versioning = manual Keep a Changelog + git tags (decided — Changesets dropped).
Remove
.changeset/, rewriterelease.ymlas the tag-triggered build/release workflow, and document the bump → changelog → tag process inCONTRIBUTING.md. (Rationale: single non-npm artifact + small team, so Changesets' monorepo/npm payoffs don't apply and its generated changelog clashes with our curated one; also matches every other QuantEcon repo. "Manual" = version + changelog + tag only — build → zip → Release stays automated by the tag trigger.) Status: done in #72 — removed.changeset/, dropped@changesets/cli+ thechangeset/versionscripts, migrated the pending entries intoCHANGELOG.md## [Unreleased], documented the manual flow inCONTRIBUTING.md, and closed the stale Changesets PR #69. The tag-triggeredrelease.yml(the build → zip → Release pipeline above) landed in #77. - Fold the
template.ymlversionbump into the release step so it can no longer drift frompackage.json(done in #77: the workflow stampstemplate.yml'sversionfrompackage.jsoninto the bundle, and fails if the tag doesn't matchpackage.json).
Cut 2.0.0 (quick deploy now, then re-release on the new flow):
- Now (decided — quick deploy first): bump
package.jsonto 2.0.0 (the@myst-theme1.3.0 upgrade, Node 24 toolchain, consolidated dependency/security updates, and technical-review fixes — all now merged tomain) and run the existingmake deployto the old build repo to get consumers off v1.1.1 today. This interim deploy is throwaway (the build repo is being retired) — accepted for speed. Done:package.jsonat 2.0.0; deployed toQuantEcon/quantecon-themeas2ca0a12(🚀 v2.0.0 from 2cf3456). - After the pipeline lands: re-publish
v2.0.0(or the next version if changes have accrued) as the first release through the new tag-triggered flow on the renamed repo, then retiremake deploy. Done 2026-06-11: changes had accrued, so the first pipeline release isv2.1.0(prepared in #79) — asset, release notes, and bundle version stamps all verified. Retiringmake deploywaits for the consumer migration below.
Consumer migration (unblocked at v2.1.0; the live consumer now tracks v2.3.0):
- Repoint the only current consumer,
QuantEcon/lecture-wasm(lectures/myst.yml:120), from…/quantecon-theme/archive/refs/heads/main.zipto the new pinned release URL (…/quantecon-theme.mystmd/releases/download/v2.1.0/quantecon-theme.zip); verifymyst start/myst build --htmlrenders it. (Done in lecture-wasm#48: verified locally viamyst start+ the PR's Netlify preview; superseded lecture-wasm#47, closed. The Netlify app's duplicate auto-build fails independently — lecture-wasm#49.) (Bumped to thev2.3.0pin in lecture-wasm#67, merged 2026-08-20, so the only consumer tracks the current release.) - Order matters — migrate the consumer before archiving the old build repo:
land pipeline → publish the first release (
v2.1.0) → repointlecture-wasm→ then archiveQuantEcon/quantecon-theme. (Order held.) - The flagship lecture repos (
lecture-python.myst,lecture-julia.myst,lecture-datascience.myst,lecture-python-advanced.myst, …) still use the Sphinxquantecon-book-themeand are not consumers yet — each gets repointed as it cuts over to MyST/JB≥2, not in this phase. (Still the case at 2026-08-20.) - (FYI, no action)
QuantEcon/workflow-backupsonly carries the repo-name globquantecon-.*, which already matches the new name.
Hygiene carried over:
- Fix naming/branding drift: package name reconciled to
@quantecon/lecture-themeacrosspackage.json+template/package.jsonin #72;template.ymltitle/sourcealigned with the renamed repo in #79. - De-duplicate the two "Development" sections in
README.mdand update the Usage URL to the pinned-release form (#79). - Triaged & consolidated the ~20 Dependabot PRs into a single lockfile refresh
(#55–58;
npm audit68 → 45). The remaining findings need the framework migration, retargeted in June 2026 from Remix v2 to React Router 7 and gated on upstreamjupyter-book/myst-theme(#28; seeSECURITY.md). - Stand up a visual preview / smoke test against a real lecture repo (the book-theme
uses
quantecon-book-theme-fixtures+ Playwright + Netlify previews — a lighter MyST equivalent would de-risk every later phase). Status: the CI gate landed in #78 — avisualjob pixel-diffs the fixture on every PR (desktop + mobile + sidebar-open), with/update-snapshotscomment-triggered baseline refresh, mirroring the book-theme's setup. The rendered preview is GitHub Pages, not Netlify (self-contained,GITHUB_TOKENonly — the org-secrets blocker is gone):preview.ymlbuildslecture-python-programmingwith the PR's theme via staticmyst build --html(the export/hydration path Playwright's livemyst startnever exercises), deploys togh-pagesunderpr-preview/pr-<n>/, sticky-comments the link, and tears down on close. The content repo is still a legacy Jupyter Book, so the build-timemyst initupgrade doubles as a migration-readiness check (open question 4). Playwright remains the only gate; the preview is qualitative.
Decisions (resolved): (1) versioning — manual Keep a Changelog + git tags (Changesets
dropped); (2) consumer URL — pinned per-lecture tag URLs
(…/releases/download/vX.Y.Z/quantecon-theme.zip); (3) 2.0.0 — quick make deploy now,
then re-release via the new pipeline; (4) execution split — the maintainer performs the
repo rename + archiving and pushes the vX.Y.Z tag via gh; the tag-triggered workflow then
creates the GitHub Release and uploads the zip asset; all code/workflow/docs land as PRs first.
Effort: M (pipeline + rename + consumer migration). Risk: low–medium (one-time
release-flow change; upstream reference implementation exists). Deps: none — best done
now, pre-cutover, while only lecture-wasm points at the theme.
Status: shipped in v2.3.0 — the build-time plugin and the header control landed together in #83. The one unchecked box below is a maintainer decision, not outstanding build work (open question 2).
Goal: reproduce the book-theme's page header: a "Last changed: <date>" control
that expands a changelog dropdown of the last N commits, with clickable commit hashes
and a "full history" link.
Reference: quantecon-book-theme/src/quantecon_book_theme/__init__.py
(get_git_last_modified, get_git_changelog, get_relative_time) +
theme/.../layout.html lines ~278–305 + assets/scripts/page-header.js +
docs/user/git-metadata.md.
Upstream (build-time) deliverable — required:
- Investigate existing
mystmdsupport for last-modified / git metadata in frontmatter before writing anything custom. Finding (2026-06-12): none built in — jupyter-book/mystmd#2213 is open with no traction. Plugin transforms can't inject frontmatter either (page frontmatter is extracted beforedocument-stage transforms run), so the plugin attaches to the page AST (mdast.data.git_metadata), which flows into the page JSON intact. Per-pagesite:frontmatter also passes through (validated as a plain object) — used as the manual override / deterministic-fixture channel. - If absent, build a MyST plugin (option A) that, during
myst build, runsgit log --followper source file and injects{ last_modified, changelog: [{hash, short_hash, author, date, message}] }. Mirrors the book-theme's git logic (5s timeout,--follow, graceful no-git/ untracked fallback,QE_GIT_METADATA_MAXfor max entries; relative time is computed at render so it can't go stale). →plugins/git-metadata.mjs, with a node-level e2e (npm run test:plugin) that builds a throwaway project with the realmystCLI. - Decide where the plugin lives. Resolved (2026-08-24, open question 2 in
#93): this repo
is the permanent home. The plugin is theme-coupled — the theme renders what it
emits — so it versions with the theme, and lecture repos load it by URL pinned to
the theme release tag they build with instead of vendoring copies:
https://raw.githubusercontent.com/QuantEcon/quantecon-theme.mystmd/<tag>/plugins/git-metadata.mjs. A sharedquantecon-myst-pluginsrepo is revisited only if a second, non-theme-coupled plugin ever appears.
Theme (render) deliverable:
- New
app/components/PageHeaderHistory.tsx(rendered fromProjectFrontmatter.tsx) that reads the injected AST data (or thesite.git_metadatapage-frontmatter override) and renders the button (aligned right of the author line) + an inline changelog, with the QuantEcon blue accent, dark mode, keyboard (Esc to close, focus returned to the trigger), and the ARIA disclosure pattern. Design settled on review (jstac + DrDrij, 2026-08-04): the panel expands above the header's blue divider, pushing it down — adjacent to its toggle and clear of the lecture content, matching the currentquantecon-book-themedropdown. An earlier centred modal was rejected. - Build commit/history URLs from the project
githubfield — commit links keep the.mystsuffix (they target the source repo, unlikeLaunchButton.tsx's notebook URLs), and the full-history link derives from the mystmd-computedsource_url. - Graceful no-op when the metadata is absent.
Effort: M–L (plugin is the bulk). Risk: medium (new upstream component). Deps: Phase 0 preview harness helps validate against a real repo.
Status: complete — the launch-config generalisation shipped in v2.2.0 via #97, and in-page Thebe in v2.3.0 via #98. Tracking issue #88 closed 2026-08-20.
Goal: match the book-theme's launch matrix. LaunchButton.tsx offered Colab +
private JupyterHub only; the book-theme also offered BinderHub (decided against,
#26) and in-page Thebe live compute.
Reference: quantecon-book-theme/src/quantecon_book_theme/launch.py (Binder/Hub/
Colab URL construction, notebook_interface, nb_path_to_notebooks, path_to_docs
stripping) + docs/user/launch.md.
- Thebe: in-page live compute enabled via
project.thebeconfig inmyst.yml. The infra was already wired (Page.tsxComputeOptionsProvider+ThebeLoaderAndServer;PageContent.tsxExecuteScopeProvider+NotebookToolbar); enablement is config-derived (compute.enabled = !!thebeFrontmatterToOptions(project.thebe), no runtime setter), so settingproject.thebesurfaces the@myst-theme/jupyterNotebookToolbar (the Power toggle, then Run/Restart/Clear once a kernel connects) on notebook pages — portaled into the QuantEcon header toolbar next to Launch (ComputeToolbarSlot.tsxrenders it viacreatePortalinto#qe-compute-slotinToolbar.tsx;app.cssneutralises the default floating pill and matches the 20px header icons), so it sits in the fixed header always visible while scrolling. The portal keeps the component inside the Thebe providers (React context flows through the React tree, not the DOM) even though the header is mounted outside them — avoiding a provider-tree lift. That header toggle is the "live compute" control. The QuantEcon default is JupyterLite (thebe: { lite: true }): Python in the browser via Pyodide, no server/Binder (avoids the flaky Binder, see #26). Verified end-to-end (Power → Pyodide kernel boots in-browser). Pyodide caveat documented in the README (numba/JAX unavailable). Fixture setsthebe.lite;tests/visual/theme.spec.tsasserts the toggle renders;notebook.png/launch-open.pngsnapshots refreshed. A second fixture (tests/visual/fixture-no-thebe/, served on a second port) asserts the toggle is absent on a notebook page when a project doesn't setproject.thebe(the disabled path). -
BinderHub: add a Binder option to theDecided against (2026-06-12): BinderHub proved flaky in practice, and Colab is the launch target QuantEcon standardises on — primarily because it provides GPU access for the lectures that need it. #26 stays open as a demand-driven future request (an implementation existed in PR #86 and was stripped before merge — recoverable from that history if demand appears). The Private JupyterHub option remains for now; its possible removal (collapsing the launcher to a direct Colab button) is tracked in #87.LaunchButtonradio group - Generalise the hardcoded
.notebookssuffix andmainbranch into config undersite.optionsinmyst.yml(MyST's analog of the book-themehtml_theme_options), so non-default branches and naming work. New optional keys (defaults reproduce the historical behaviour, so existing lectures are unchanged):launch_repo_suffix(default.notebooks),launch_branch(defaultmain), pluslaunch_repo_urlto point at an arbitrary notebook repo (book-themenb_repository_urlparity). URL logic extracted to pure, unit-tested builders inlaunchUrls.ts; applies to both the Colab and the Private-JupyterHub launch URLs. Done in #97. - Fix Colab/JupyterHub path handling for nested lecture dirs: strip the source
extension robustly (the old
page.location.split('.')[0]truncated paths/dirs containing a dot), striplaunch_source_path(book-themepath_to_docs) and prependlaunch_notebooks_path(book-themenb_path_to_notebooks). Covered bytests/unit/launch-urls.test.mjs(same PR, #97).
Note: removing the Private JupyterHub option (collapse to a single Colab button, #87) is deliberately kept out of this work — it's gated on a maintainer demand signal — so the generalisation above covers both Colab and the hub. See #87.
Effort: M. Risk: low–medium. Deps: Phase 0.
Two related theming features; bundle together since both touch the CSS/token layer.
Goal A — configurable code highlighting: book-theme qetheme_code_style toggles
between custom QuantEcon token colours and any built-in Pygments style.
Goal B — text colour schemes: book-theme ships seoul256 (default), gruvbox,
none, plus a custom_color_scheme.css hook, exposed as CSS variables
(--qe-literal-color, emphasis/strong/definition colours).
Reference: book-theme __init__.py (add_pygments_style_class,
setup_pygments_css, validate_color_scheme), assets/styles/_color-schemes.scss /
_syntax.scss, docs/user/code-highlighting.md, docs/user/text-color-schemes.md.
- Map the book-theme colour-scheme CSS variables onto this theme's Tailwind tokens
(
qetext-*,qeborder-blue, …) intailwind.config.js/styles/app.css. - Add a
myst.yml-driven option to select scheme; render a body/root class (color-scheme-gruvbox, etc.) the same way the book-theme does. - Decide code-highlighting strategy under MyST (syntax highlighting comes from
@myst-theme/prism, not Pygments) — likely "QE token theme vs upstream default" rather than a literal Pygments port. Document the mapping.
Effort: M. Risk: medium (visual regressions — needs the Phase 0 preview). Deps: Phase 0.
Goal: book-theme v0.20.0 globe-icon dropdown to switch between translated lecture
sites, plus <link rel="alternate" hreflang> head tags for SEO. Only renders with 2+
languages configured.
Reference: book-theme _process_languages() in __init__.py, layout.html
hreflang block + language-switcher markup, assets/scripts/language-switcher.js,
assets/styles/_language-switcher.scss, docs/user/rtl-support.md,
docs/developer/multilingual.md + infrastructure.md.
- Add a
languagesconfig (list of{code, name, url}) tomyst.yml/site config, surfaced to the theme via the site manifest loader (loaders.server.ts). - New toolbar
LanguageSwitcher.tsx(Radix dropdown), placed consistently inToolbar.tsx/MobileActionsMenu.tsx, with keyboard nav + active-language marker. - Inject
hreflangalternates inroot.tsxlinks/metafor each language +x-default.
Effort: M. Risk: low. Deps: none (independent of 1–3).
Goal: book-theme enable_rtl sets dir="rtl" on <body> and ships _rtl.scss.
Reference: book-theme layout.html body_tag block + assets/styles/_rtl.scss +
docs/user/rtl-support.md.
- Add a config flag; set
dir="rtl"on the document inroot.tsx. - Audit Tailwind utilities for logical-property / RTL correctness (margins, the blue left/right accents, toolbar ordering); add RTL overrides where physical properties leak.
Effort: S–M. Risk: low. Deps: ideally after Phase 4 (often shipped together for the same translated sites).
Goal: close the smaller gaps and verify assumptions.
- OpenGraph/Twitter parity: the book-theme emits a full OG + Twitter card set;
root.tsxcurrently usesgetMetaTagsForSite(title/description/twitter). Addog:image/twitter:image(logo),og:type,og:site_name, etc., driven from site config. - Collapsible stderr warnings: confirm whether
@myst-theme/jupyteralready renders notebook stderr in a collapsible/styled way (it may — verify before porting). If not, add an output transform/renderer. - Docs: add a
docs/-style feature reference for the MyST theme mirroring the book-theme'sdocs/user/*set, so downstream lecture maintainers have parity documentation.
Effort: S–M. Risk: low. Deps: none.
Phase 0 (hygiene/deploy + preview harness) ── prerequisite ✅ shipped
│
├─▶ Phase 1 Git history in headers ⭐ ✅ shipped v2.3.0
├─▶ Phase 2 Launch parity (Thebe + config) ✅ shipped v2.2.0 / v2.3.0
├─▶ Phase 3 Code highlight + colour schemes (needs visual preview) ← next
├─▶ Phase 4 i18n (language switcher) ──▶ Phase 5 RTL (commonly shipped together)
└─▶ Phase 6 Meta/SEO + stderr + docs
Suggested order: 0 → 1 → 2 → 3 → (4 → 5) → 6. Phases 0–2 are shipped as of v2.3.0 (2026-08-20), so Phase 3 is next. Phases 3–6 are largely independent of one another and can be parallelised across contributors. Per open question 4's resolution below, all of Phases 3–6 gate the all-at-once lecture migration — they are cutover blockers, not optional polish.
Tracked in #93, closed 2026-08-24 with all four questions decided; the resolutions are recorded inline below.
- Upstream-first? Several of these (git metadata rendering, language switcher,
RTL) could be contributed to
jupyter-book/myst-themerather than kept QuantEcon-only. Which features are worth upstreaming vs forking? Resolved (2026-08-24): local-first. Every feature lands in this repo first; upstreaming is a deferred later phase, so it never blocks deploying and upgrading mystmd for the lecture series. Candidates are tracked inUPSTREAM-CANDIDATES.yml, a feature-level registry with the same status lifecycle as the QuantEconmystmdfork'squantecon/UPSTREAM-PRS.yml— but with local PRs recorded as provenance rather than cherry-pick lists, since this repo is not a fork ofjupyter-book/myst-themeand features must be ported, not replayed. - Plugin home: should the build-time git-metadata plugin (Phase 1) live in a shared
quantecon-myst-pluginsrepo consumed by every lecture repo, or per-repo? Resolved (2026-08-24): this repo, permanently. Only one plugin exists (plugins/git-metadata.mjs) and it is theme-coupled, so it stays here and versions with the theme; lecture repos reference it by tag-pinned URL rather than vendoring copies. A shared plugins repo is revisited only if a second, non-theme-coupled plugin ever appears. - Compute model: how much of the launch story moves to in-page Thebe vs external
Colab/Binder/Hub, given infra cost and the existing
.notebooksconvention? Answered in practice (2026-08-20): external launch is Colab (BinderHub dropped, #26; the private hub stays for now, #87), and in-page compute is Thebe with JupyterLite/Pyodide as the QuantEcon default — both shipped in Phase 2. What remains has its own carriers: toggle placement and integration (#128) and per-lecture enablement with compatibility testing (#114). - Scope for first migration: which lectures migrate to JB≥2 first, and which subset of phases must land before that cutover (Phase 1 git-history is likely a must-have)? Resolved (2026-08-24): all-or-nothing. No per-lecture staging — every lecture series migrates at once, onto a single common publishing base, once feature parity is complete (single maintenance surface). The cutover gate is therefore the full remaining milestone — Phases 3–6 (#89–#92) — plus the open live-compute defect #117.