Background
ci.yml's JupyterLite build pins jupyterlite-pyodide-kernel==0.6.1 (bundling Pyodide 0.27.6) deliberately, to stay behind Pyodide's default-on WebAssembly JSPI ("stack switching", on by default since 0.27.7) — Safari has no JSPI support as of 2026-08, Firefox only behind a flag. Without this pin, jupyterlite-pyodide-kernel's use of run_until_complete() either crashes the kernel outright in those browsers, or (with the enableRunUntilComplete: false workaround) silently races package installs. Full rationale in the toolbox-maintainer skill's JupyterLite version-pinning section. Same pin, same reason, on MVTB and bdsim.
As of the pure-Python-wheel fix (see the PR this issue was filed alongside), RTB's own wasm wheel no longer has any ABI coupling to a specific Pyodide/Emscripten version — a py3-none-any wheel works under any Pyodide version. So the pin is no longer needed for our own package's sake. It's still needed for the kernel-crash/JSPI reason above, independent of anything RTB ships.
The remaining cost of staying pinned: everything Pyodide bundles internally (numpy, scipy, etc., for anyone micropip.install-ing them in a live notebook) stays frozen at whatever versions Pyodide 0.27.6 shipped, indefinitely — until Safari/Firefox ship JSPI for real.
Proposed idea (not yet designed/committed to)
A lightweight client-side dispatcher page in front of the JupyterLite "Try it Now" link: feature-detect real JSPI support (typeof WebAssembly.Suspending === "function") and redirect to one of two parallel JupyterLite deployments — a modern build (current kernel/Pyodide, JSPI on) for browsers that support it, and today's pinned legacy build for those that don't. Feature detection (not user-agent sniffing) means Safari/Firefox users automatically graduate to the modern build the day their browser ships JSPI, with no code changes needed here.
Why not done now
This is a real but secondary problem — it predates and is independent of the pure-Python-wheel fix, which was this session's actual scope. The dispatcher approach is a genuine scope increase: two parallel JupyterLite deployments, extra CI build time, extra static hosting, a dispatcher page, and testing both paths. Per the toolbox-maintainer skill's cross-repo consistency principle, doing this "properly" means MVTB and bdsim too, not just RTB, since all three carry the identical pin for the identical reason.
Next steps, if picked up
- Confirm
WebAssembly.Suspending is still the correct JSPI feature-detection API at the time this is implemented.
- Decide whether to build this once as a shared pattern (e.g. documented in
rvc-ecosystem) rather than three independent implementations.
- Weigh the hosting/CI cost against the actual benefit (how stale do Pyodide's bundled scientific packages actually get in practice, and does anyone's notebook usage actually hit that staleness).
Background
ci.yml's JupyterLite build pinsjupyterlite-pyodide-kernel==0.6.1(bundling Pyodide 0.27.6) deliberately, to stay behind Pyodide's default-on WebAssembly JSPI ("stack switching", on by default since 0.27.7) — Safari has no JSPI support as of 2026-08, Firefox only behind a flag. Without this pin,jupyterlite-pyodide-kernel's use ofrun_until_complete()either crashes the kernel outright in those browsers, or (with theenableRunUntilComplete: falseworkaround) silently races package installs. Full rationale in the toolbox-maintainer skill's JupyterLite version-pinning section. Same pin, same reason, on MVTB and bdsim.As of the pure-Python-wheel fix (see the PR this issue was filed alongside), RTB's own wasm wheel no longer has any ABI coupling to a specific Pyodide/Emscripten version — a
py3-none-anywheel works under any Pyodide version. So the pin is no longer needed for our own package's sake. It's still needed for the kernel-crash/JSPI reason above, independent of anything RTB ships.The remaining cost of staying pinned: everything Pyodide bundles internally (numpy, scipy, etc., for anyone
micropip.install-ing them in a live notebook) stays frozen at whatever versions Pyodide 0.27.6 shipped, indefinitely — until Safari/Firefox ship JSPI for real.Proposed idea (not yet designed/committed to)
A lightweight client-side dispatcher page in front of the JupyterLite "Try it Now" link: feature-detect real JSPI support (
typeof WebAssembly.Suspending === "function") and redirect to one of two parallel JupyterLite deployments — a modern build (current kernel/Pyodide, JSPI on) for browsers that support it, and today's pinned legacy build for those that don't. Feature detection (not user-agent sniffing) means Safari/Firefox users automatically graduate to the modern build the day their browser ships JSPI, with no code changes needed here.Why not done now
This is a real but secondary problem — it predates and is independent of the pure-Python-wheel fix, which was this session's actual scope. The dispatcher approach is a genuine scope increase: two parallel JupyterLite deployments, extra CI build time, extra static hosting, a dispatcher page, and testing both paths. Per the toolbox-maintainer skill's cross-repo consistency principle, doing this "properly" means MVTB and bdsim too, not just RTB, since all three carry the identical pin for the identical reason.
Next steps, if picked up
WebAssembly.Suspendingis still the correct JSPI feature-detection API at the time this is implemented.rvc-ecosystem) rather than three independent implementations.